Powershell Pull AD Group and Subgroup Endless Loop - powershell

My script is hitting the active directory to pull AD groups and their subgroups.
I will be adding in Get-ADGroupMember Where-Object { $_.ObjectClass -eq "user" }) to the script but right now the script runs endlessly.
I checked the CSV file and data is being pushed into it, I just do not know why this is running endlessly.
I am still new to Powershell and may have overlooked something.
Do I need to add some sort of catch or exit? I read that exit closes the loop before it can execute completely.
My Script:
Clear-Host
#Import-Module ActiveDirectory
Write-Host -ForegroundColor Green "Starting"
$logfile = "C:\temp\logfile.csv"
"Domains,Groups,Nested" | Add-Content $logfile
$grp_list = Import-Csv "C:\temp\Copy_lp.csv"
$domains = #()
$dom = Import-Csv "C:\temp\Copy_lp.csv" |
Select-Object -Unique -ExpandProperty "Domain Group"
foreach ($domain in $dom) {
$domains += $domain.Split('\')[0]
}
$domains = $domains | Select-Object -Unique
foreach ($domain in $domains) {
Write-Host "Domain:" $domain
Get-ADGroup -Filter * -Server $domain |
ForEach-Object {
$group = $_.Name
$grps = (Get-ADGroupMember $group -Server $domain |
Where-Object { $_.ObjectClass -eq "Group" })
$grps | ForEach {
Write-Host "Group: " $_.Name
"$domain,$($_.Name),$group" | Add-Content $logfile
}
}
}
Write-Host -ForegroundColor Green "Done"
Update
This was answered by Tomer. I had taken the output CSV from a previous script that was executed to look at user permissions at the database level. So when a user was mapped to multiple databases (user also belonged to different groups and subgroups) within the instance and would appear numerous times. So as my script went through the list it would re-run the same AD Group(s) over and over. I still do not understand why it would continuously run but once I altered the CSV file to have just a domain list it executed and spit out the output I was looking for. I did not put this as the answer because I felt Tomer deserved the credit. Thank you all for your help.

In Active Directory, membership self-loops are permitted. So for example, group A may contain another group B which may contain group A again. Therefore, simple enumeration of the member-of relation can lead to an infinite loop.
An easy way to solve it is to store the DNs you've already seen, and not going over them again.

Related

Trying to remove a Computer Object from AD security group using powershell & importing from a CSV file

having a heck of a time with this. Also, wanted to preface this with I'm not the best at PowerShell as I'm just starting out. I have a CSV file that I'm trying to read the first column which happens to be 'AssetName'. These are AD joined computers.
#Get Computer
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object AssetName
$Group = "Sec Group Name"
# Set the ErrorActionPreference to SilentlyContinue, because the -ErrorAction
# option doesn't work with Get-ADComputer or Get-ADGroup.
$ErrorActionPreference = "SilentlyContinue"
# Get the computer and group from AD to make sure they are valid.
$ComputerObject = Get-ADComputer $Computer
$GroupObject = Get-ADGroup $Group
Foreach ($Computer in $Computers){
if ($GroupObject) {
# If both the computer and the group exist, remove the computer from
# the group.
Remove-ADGroupMember $Group `
-Members (Get-ADComputer $Computer).DistinguishedName -Confirm:$False
Write-Host " "
Write-Host "The computer, ""$Computer"", has been removed from the group, ""$Group""." `
-ForegroundColor Yellow
Write-Host " "
}
else {
Write-Host " "
Write-Host "I could not find the group, ""$Group"", in Active Directory." `
-ForegroundColor Red
Write-Host " "
}
}
else {
Write-Host " "
Write-Host "I could not find the computer, $Computer, in Active Directory." `
-ForegroundColor Red
Write-Host " "
}
Upon doing so, I want to remove that Asset from a specific security group. Whenever I run my script, I get this error. I don't know why it's reading it with the "#{AssetName=CompName}".
The computer, "#{AssetName=CompName}", has been removed from the group, "Sec Group Name".
Any help would be much appreciated.
With your first line you are saving a list of PSObjects to $Computers.
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object AssetName
These Objects look like this #{AssetName=Computername}
When you iterate these objects you need to specify that you only want the value of the AssetName parameter
Get-ADComputer $Computer.AssetName
Another (in my opinion better) way would be to stop using Select-Object (which storing the returned objects in $computers) and only storing a list of AssetNames in $computers like this:
$Computers = (Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv').AssetName
EDIT:
You can also use -ExpandProperty with your Select-Object:
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object -ExpandProperty AssetName

Remove groups from a CSV that contains folders and the list of people who has access

I have no idea what I am doing with powershell. I am still very new to scripting, and have always had trouble the few times I've had to use it.
At work I'm trying to clean up our shared directories that have individual permissions on them. I've have a CSV file that contains a list of directories, and any users & groups that have access to those directories.
The CSV file has information looking like this:
PATH Identity
Folder1 AD\User1
Folder1 AD\Group1
Folder2 AD\User2
Folder2 AD\Group2
Folder3 AD\Group3
etc. It goes on similarly for about 1,800 entries. The problem is I need to filter any groups from this list and, if there are any folders that only have group access & no individual access then remove those from the list as well.
I was trying to make a new script to pull that csv into powershell & filter it, but no idea how to go about it.
I was trying this, but definitely don't think it's the right way to go about it:
$permissions = Import-csv C:\Users\MyUser\Desktop\Permissions.csv | select Path,Identity | ft
$permissions | ForEach($_.Identity) {
$users Get-ADObject -Identity $_.Identity
if($users.ObjectClass -eq "user"){
select Path,Identity
}
}
Export-Csv C:\Users\MyUser\Desktop\groupsRemoved.csv
Not actually sure what you are attempting to do.
Seems like you want to save any of the shares that have user accounts instead of group accounts assigned.
This should do that.
$csv = Import-Csv -Path C:\Users\MyUser\Desktop\Permissions.csv | Select Path, Identity
foreach($row in $csv){
$user = $row.Identity
$share = $row.Path
if(Get-ADUser -Filter {Name -eq $user} -ErrorAction SilentlyContinue){
[PSCustomObject]#{
User = $user
Share = $share
} | Export-Csv C:\Users\MyUser\Desktop\groupsRemoved.csv -NoTypeInformation -Append
}
else{
Write-Host "$user is a Group"
}
}
Alternatively, if you wanted to get the permissions in real-time, you can do this.
$csv = Import-Csv -Path C:\Users\MyUser\Desktop\Permissions.csv | Select Path
foreach($share in $csv){
#Get list of users with permissions to share
$shareUsers = (Get-SmbShareAccess -Name $share).AccountName
foreach($user in $shareUsers){
$user = ($user.Split('\'))[1]
if(Get-ADUser -Filter {Name -eq $user} -ErrorAction SilentlyContinue){
[PSCustomObject]#{
User = $user
Share = $share
} | Export-Csv C:\Users\MyUser\Desktop\groupsRemoved.csv -NoTypeInformation -Append
}
else{
Write-Host "$user is a Group"
}
}
}

Powershell script that will copy files to users in different groups - each group gets different files

As a beginner I am having trouble with a rather straightforward task. I need to write a ps script that will copy files to users in different groups - each group gets different files. All files are on a network location, so I don't need to do anything on their computers.
First idea is to get a variable with all active users and then check to which groups they belong. If they belong to one of the three groups the script will copy the necessary files to their network location. There is a problem with one group for managers, as they belong to two groups that I am interested in, but I can probably fix that with conditions.
Most of the code I written for this are just snipets as the one below, trying to test PS out:
$Source = Get-ADUser -LDAPFilter "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))" #Import active users as a source variable
foreach ($user in $Source) #for every user in source get his groups
{
$UserGroups = get-aduser $user -Properties memberof | select -expand memberof
If ($user.IsMember("LDAP://" + ) -eq $False)
{
Write-Host "He is a member!"
}
I know it is not much, I have been searching the net for a suitable examples for over a week now with no luck.
Here is an example of how to test membership for a group:
$targetGroup1 = "Administrators"
$targetGroup2 = "Domain Users"
Get-ADUser -Filter * -Properties MemberOf | ForEach-Object {
if(($_.MemberOf -match "CN=$targetGroup1") -and ($_.MemberOf -match "CN=$targetGroup2")) {
"$($_.Name) is a member both groups"
"copying files from a special location"
} elseif($_.MemberOf -match "CN=$targetGroup1") {
"$($_.Name) is a member of $targetGroup1"
"copying files from source1"
} elseif($_.MemberOf -match "CN=$targetGroup2") {
"$($_.Name) is a member of $targetGroup2"
"copying files from source2"
}
}
Something like this maybe (code is untested):
Import-Module ActiveDirectory
$groupOneMembers = Get-AdGroupMember Group1 | Select-Object -ExpandProperty SamAccountName
$groupTwoMembers = Get-AdGroupMember Group2 | Select-Object -ExpandProperty SamAccountName
$groupThreeMembers = Get-AdGroupMember Group3 | Select-Object -ExpandProperty SamAccountName
foreach($member in $groupOneMembers) {
Copy-Item -Recurse \\server\GroupOneFiles \\server\Users\$member
}
foreach($member in $groupTwoMembers) {
Copy-Item -Recurse \\server\GroupOneFiles \\server\Users\$member
}
foreach($member in $groupThreeMembers) {
Copy-Item -Recurse \\server\GroupOneFiles \\server\Users\$member
}

Powershell script extract AD group members - find last-login time for each user

I'm trying to create a script that will pull a list of group members from AD and run a foreach loop to determine when the last time each user logged into any given domain controller. I got some of the code for the measure-latest function here . I would like to have the script run through the foreach loop and print the samAccountName (username) and last login time stamp (measure-latest) for each user in the group, but so far have not been able to get it working. I think I've got something wrong in logic but i can't seem to figure it out. Any help is appreciated, thank you.
# Get a list of last login times for a group of users
# Script Requires Quest Cmdlet features: https://support.software.dell.com/activeroles-server/download-new-releases
Add-PSSnapin Quest.ActiveRoles.ADManagement
# filter out $nulls and produce the latest of them
function Measure-Latest {
BEGIN { $latest = $null }
PROCESS {
if (($_ -ne $null) -and (($latest -eq $null) -or ($_ -gt $latest))) {
$latest = $_
}
}
END { $latest }
}
# Get list of group users by username
Get-ADGroupMember -identity "Domain Admins" | select samAccountName | Export-csv -path C:\Scripts\UserInformationByGroup\Groupmembers.csv -NoTypeInformation
# Get list of users from group, assign user value
$userlist = import-csv C:\Scripts\UserInformationByGroup\Groupmembers.csv
$user = $userlist | Select samAccountName
# Loop through list of users and print Username ------ Last Login time
foreach ($user in $userlist) {
Get-QADComputer -ComputerRole DomainController | foreach {
(Get-QADUser -Service $_.Name -SamAccountName $user).LastLogon
} | Measure-Latest $samAccountName | out-file -filepath C:\Scripts\UserInformationByGroup\userListLastLogin.txt -append
}
I should mention that when I run the script like this, and just enter each username manually it works and prints the last login time:
Add-PSSnapin Quest.ActiveRoles.ADManagement
function Measure-Latest {
BEGIN { $latest = $null }
PROCESS {
if (($_ -ne $null) -and (($latest -eq $null) -or ($_ -gt $latest))) {
$latest = $_
}
}
END { $latest }
}
Get-QADComputer -ComputerRole DomainController | foreach {
(Get-QADUser -Service $_.Name -SamAccountName USER_NAME_HERE).LastLogon
} | Measure-Latest
This part of the pipeline makes no sense:
| Measure-Latest $samAccountName |
Since nothing is assigned to the variable $samAccountName
You'll need to add pipeline support to your Measure-Latest function, like so:
function Measure-Latest {
[CmdletBinding]
param(
[parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
$LastLogon
)
# rest of the script (begin-process-end blocks) goes here
}
And now you can pipe the LastLogon values directly to the function:
(Get-QADUser -SamAccountName $user).LastLogon |Measure-Latest | Out-File #or whatever
Thanks to the ValueFromPipelineByPropertyName flag, you can now also pipe the entire object to the function:
Get-QADUser -SamAccountName $user | Measure-Latest
Because the parameter name ($LastLogon) matches that property anyways
In addition to that, I would probably change the logic a bit, so that you don't perform 1 LDAP query per DC per user. If you have 5 Domain Controllers and 200 users, that's now 1000 individual queries to the directory service.
You could simply retrieve ALL the users from each DC, in a single query, using the -LdapFilter parameter with Get-QADUser:
$LDAPClauses = #()
foreach($user in $userlist){
$LDAPClauses += "(samaccountname={0})" -f $user
}
# The | in an LDAP filter means logical OR
$LDAPFilter = "(&(LastLogon=*)(|$(-join($LDAPClauses))))"
Now you can run just a single query per DC:
Get-QADUser -LdapFilter $LDAPFilter
and it'll retrieve all the users in $userlist that has a LastLogon attribute value on that specific DC (you're not really interested in $null-values anyways, right?)

Powershell Script to filter computer names and check for group membership and save

This is a really noob question; the purpose of the script is to sweep the domain for computers that begin with something. Then to check the computers for group membership and give results for computers that are not part of a specific group. And then email. But this is where I am having an issue. I am trying to get the information from my if statement into an email. But for the sake of simplicity I just want the output it in a file for now.
Here is what I have:
# Load the Microsoft Active Directory Module
Import-Module ActiveDirectory
# Get a list of computers that begin with Funk
$Computers = Get-ADComputer -filter "name -like 'Funk*'" | ForEach-Object {$_.Name}
# Identify which computers do not have "XYZGroup" group
$Group = "XYZGroup"
$Groupname = (Get-ADGroup $Group).distinguishedName
ForEach ($adcomputer in ($computers))
{
If ((Get-ADComputer $adcomputer -Properties MemberOf | Select -ExpandProperty MemberOf) -notcontains $Groupname)
{
"Group is not present for $adcomputer"
}
}
As you can see I don't currently have anything in there to actually output the data. Any input? Thanks
Good lord, you're making things hard on yourself. First, you're getting all those computers' objects twice. Don't do that, just don't discard all the useful info on the first query. Or better yet, filter the first query, and only get the computers you really wanted in the first place. Check this out:
Import-Module ActiveDirectory
$Group = "XYZGroup"
$Groupname = (Get-ADGroup $Group).distinguishedName
$Computers = Get-ADComputer -filter "name -like 'Funk*'" -Prop MemberOf| Where{$_.MemberOf -notcontains $Groupname}
$computers |Select -Expand Name|Out-File 'C:\Path\To\Output.txt'
ForEach($Computer in $Computers){
Send-MailMessage -From Me#company.com -To Someone#company.com -Subject "Rogue Computer!" -Body "$Computer is not in $Groupname"
}
Now you'll need to tweak the send-mailmessage cmdlet line, but that sounds like it does everything you wanted it to.
Edit: Ok, to get all the computers you could do something like this:
$CompList = $computers.name -join ", "
That gives you 'Computer1, Computer2, Computer3' as a string. Or if you want one per line you could use
$CompList = $computers.name -join "`n"
That would result in a multi-line string like:
Computer1
Computer2
Computer3
The nice part about that is that when it comes to send your email you just do something like:
Send-MailMessage -To Somebody#example.com -from me#example.com -subject "Computers not in $groupname" -Body "$CompList"
and the body is just the list of computer names one per line for easy copy/paste or whatever.
So is the question how to output data into a file?
One of the ways is appending >> filename.fileextension to whatever you want to output
Heads up this will NOT overwrite the file entirely, it will append whatever it is you chose, to the file's content.
For example:
# Load the Microsoft Active Directory Module
Import-Module ActiveDirectory
# Get a list of computers that begin with Funk
$Computers = Get-ADComputer -filter "name -like 'Funk*'" | ForEach-Object {$_.Name}
# Identify which computers do not have "XYZGroup" group
$Group = "XYZGroup"
$Groupname = (Get-ADGroup $Group).distinguishedName
ForEach ($adcomputer in ($computers))
{ If ((Get-ADComputer $adcomputer -Properties MemberOf | Select -ExpandProperty MemberOf) -notcontains $Groupname)
{
"Group is not present for $adcomputer" >> result.txt
}
}