Powershell - printing out during a loop - powershell

I have to go through an OU and remove all the memberOf groups for all users. The script I have works, but I cannot get it to print out when each user is processed. So when I run the script, it working, but nothing happens until its complete. For piece of mind, I want to SEE the current login name that is being processed, but I can't, for the life of me, get the write-host command to write to my screen.
Here is my code:
$users = get-aduser -filter * -searchbase "ou=disabled,dc=corp,dc=test,dc=org" | Sort-Object -Property Name
Function removeMemberShips {
Param( [string] $SAMAccountName)
$user = get-aduser $SAMAccountName -properties memberof
$userGroups = $user.memberof
$userGroups | %{get-adgroup $_ | Remove-ADGroupMember -confirm:$false -member $SAMAccountName}
$userGroups = $null
}
$users | %{removeMemberShips $_.SAMAccountName}

The most obvious way seems to be:
$users | %{
removeMemberShips $_.SAMAccountName
Write-Host $_.SAMAccountName
}

Related

How to move adobject to another OU

i want to know how to move adobject to another ou?
Im not sure what im doing wrong with the -filter
$CSVFiles = #"
adobject;
StdUser
TstUser
SvcAcc
"# | Convertfrom-csv -Delimiter ";"
$TargetOU = "OU=ARCHIVE,DC=contoso,DC=com"
foreach ($item in $CSVFiles){
get-adobject -Filter {(cn -eq $item.adobject)} -SearchBase "OU=ADMIN,DC=contoso,DC=com"| select distinguishedname | Move-ADObject -Identity {$_.objectguid} -TargetPath $TargetOU
}
-Identity { $_.ObjectGuid } shouldn't be there, this parameter can be bound from pipeline, you also do not need to strip the objects from it's properties, in other words, Select-Object DistinguishedName has no use other than overhead.
Also filtering with a script block (-Filter { ... }) is not well supported in the AD Module and should be avoided. See about_ActiveDirectory_Filter for more info.
$TargetOU = "OU=ARCHIVE,DC=contoso,DC=com"
foreach ($item in $CSVFiles){
$adobj = Get-ADObject -LDAPFilter "(cn=$($item.adobject)" -SearchBase "OU=ADMIN,DC=contoso,DC=com"
if(-not $adobj) {
Write-Warning "'$($item.adobject)' could not be found!"
# skip this object
continue
}
$adobj | Move-ADObject -TargetPath $TargetOU
}

Powershell Script to remove disabled accounts from groups runs slow. How to make it faster?

My code:
$searchOU = "OU=a,OU=b,OU=c,OU=d,OU=e,DC=f,DC=g,DC=com"
Get-ADGroup -Filter 'GroupCategory -eq "Security"' -SearchBase $searchOU | sort name | ForEach- Object{
$group = $_
Get-ADGroupMember -Identity $group | Get-ADUser | Where-Object { $_.Enabled -eq $false} | ForEach-Object{
$user = $_
$uname = $user.Name
$gname = $group.Name
Write-Host "Removing $uname from $gname" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Member $user -Confirm:$false #-whatif
}
}
It runs, but it's dog slow. Any suggestions on ways to make it run faster?
You need to remember that Get-ADGroupMember can return users, groups, and computers, not just user objects..
If you want to search for user objects only, you need to add a Where-Object clause there.
Unfortunately, while Get-ADUser has a -Filter parameter that enables you to find disabled users much more quickly than filtering afterwards on the collection of users, using a filter while piping user DN's to it will totally ignore the pipeline and collect all users that are disabled..
So, in this case, we're stuck with appending a Where-Object clause.
You could change your code to rule out all objects from Get-ADGroupMember that are not uders:
$searchOU = "OU=a,OU=b,OU=c,OU=d,OU=e,DC=f,DC=g,DC=com"
Get-ADGroup -Filter "GroupCategory -eq 'Security'" -SearchBase $searchOU | Sort-Object Name | ForEach-Object {
$group = $_
Get-ADGroupMember -Identity $group | Where-Object { $_.objectClass -eq 'user' } |
Get-ADUser | Where-Object { $_.Enabled -eq $false} | ForEach-Object {
Write-Host "Removing $($_.Name) from $($group.Name)" -Foreground Yellow
Remove-ADGroupMember -Identity $group -Member $_ -Confirm:$false #-whatif
}
}
The above removes one disabled user at a time and for each of them writes a line on the console.
You could make it work faster if you can cope with getting a different output on screen like this:
$searchOU = "OU=a,OU=b,OU=c,OU=d,OU=e,DC=f,DC=g,DC=com"
$result = foreach ($group in (Get-ADGroup -Filter "GroupCategory -eq 'Security'" -SearchBase $searchOU)) {
$users = Get-ADGroupMember -Identity $group | Where-Object { $_.objectClass -eq 'user' } |
Get-ADUser | Where-Object { $_.Enabled -eq $false}
if ($users) {
# the Remove-ADGroupMember cmdlet can take an array of users to remove at once
Remove-ADGroupMember -Identity $group -Member $users -Confirm:$false #-whatif
# output an object that gets collected in variable $result
[PsCustomObject]#{Group = $group.Name; RemovedUsers = ($users.Name -join '; ')}
}
}
# if you like, output to console as table
$result | Sort-Object Group | Format-Table -AutoSize -Wrap
# or write to CSV file
$result | Export-Csv -Path 'D:\Test\RemovedUsers.csv' -NoTypeInformation

Script needs converting to export to CSV

I put together the below, which does the job. However, the output isn't very workable. So I wanted to output this all to a CSV using Export-Csv. Im aware I can do this by moving to a ForEach-Object query, but im not entirely sure how to achieve that.
I have added an attempt to convert it in hopes of a little help. I'm not sure how to specify the variable for each object. For example the first section calls all domains in the forest. How do i use each response in the next piped query? and so on.
$domains = (Get-ADForest).Domains
$controllers = #()
$worked = $false
foreach ($domain in $domains) {
$controller = Get-ADDomainController -Discover -ForceDiscover -DomainName $domain |
Select-Object HostName
$controllers += $controller
}
while (-not $worked) {
try {
foreach ($item in $controllers) {
$value = $item.HostName.Value
Write-Host $value
Write-Host 'Domain Admins'
Get-ADGroupMember -Identity 'Domain Admins' -Server $value |
Get-ADUser -Properties name, samaccountname, Description, EmailAddress |
Where {$_.Enabled -eq $true} |
Format-Table Name, SamAccountName, Description, EmailAddress -AutoSize
}
$worked = $true
} catch {}
}
Conversion Attempt
ForEach-Object{
(Get-ADForest).domains | Get-ADDomainController -Discover -ForceDiscover -DomainName $domain |Select-Object HostName | Get-ADGroupMember -identity 'Domain Admins' -Server $value | Get-ADUser -Properties samaccountname, Description, EmailAddress | Where {$_.Enabled -eq $true}
}| Export-Csv -Path "$HOME/Desktop/DomainAdmins.csv" samaccountname, Description, EmailAddress -AutoSize
If you can get the values from your Get-ADUser call and put them in an object, you can then pipe to convertto-csv.
Here's an example:
$arr = #([pscustomobject]#{name="name"; sam="samaccountname"}, [pscustomobject]#{name="name2"; sam="samaccountname2"});
$arr | ConvertTo-Csv -NoTypeInformation
"name","sam"
"name","samaccountname"
"name2","samaccountname2"
You could get rid of the Format-Table call. The code I've shown in the example pipes and array of objects into the convertto-csv cmdlet. So if Get-ADUser returns objects, you should be able to pipe right into ConvertTo-CSV or Export-Csv -append
The objects are hashtables that are cast to pscustomobjects, it's a nice quick way to illustrate the technique.
The result, as shown, will be csv headers that match your hashtable keys, and the hastable values will be the CSV values.
This is working fine in my local environment and storing the result in D:\Test_File.csv
$domains = (Get-ADForest).Domains
$controllers = #()
$worked = $false
foreach ($domain in $domains) {
$controller = Get-ADDomainController -Discover -ForceDiscover -DomainName $domain | Select-Object HostName
$controllers += $controller
}
while (-not $worked) {
try
{
foreach ($item in $controllers)
{
$value = $item.HostName.Value
Write-Host $value
Write-Host 'Domain Admins'
Get-ADGroupMember -Identity 'Domain Admins' -Server $value |
Get-ADUser -Properties name, samaccountname, Description, EmailAddress |?{$_.Enabled -eq $true}|Export-Csv -Append "D:\Test_File.csv"
}
#$worked = $true
}
catch
{
$Error_Message=$_.Exception.Message
}
}

Powershell Adding users within groups to cross forest groups

This script works without error now, but the problem is that when several groups in the searchbase are found, the script will add all users from all groups to the cross forest target groups.
So for example:
ForestAGroup1 = contains 2 users
ForestAGroup2 = contains 2 users
::runs script::
now...
ForestBGroup1 = contains 4 users
ForestBGroup2 = contains 4 users
The ForestBGroup1/2 needs to contain the same identical users as ForestAGroup1/2.
Here is the script for reference:
$creds = Get-Credential
$Groups = Get-ADGroup -Properties * -Filter * -SearchBase "OU=TEST,OU=Shop Print Groups,OU=User,OU=domain Groups,DC=domainA,DC=com" | export-csv c:\temp\test.csv
$Groups = Get-ADGroup -Properties * -Filter * -SearchBase "OU=TEST,OU=Shop Print Groups,OU=User,OU=domain Groups,DC=domainA,DC=com"
Foreach($G In $Groups)
{
#Display group members and group name
Write-Host $G.Name
Write-Host "-------------"
$G.Members
#Add members to domainB group
$domainGMembers = import-csv C:\temp\test.csv | ForEach-Object -Process {Get-ADGroupMember -Identity $_.CN} | Select-Object samaccountname | export-csv c:\temp\gmembers.csv
$domainDNUser = import-csv C:\temp\gmembers.csv | ForEach-Object -Process {Get-ADUser $_.samaccountname -Server "domainA.com" -properties:Distinguishedname}
import-csv C:\temp\gmembers.csv | ForEach-Object -Process {Add-ADGroupMember -Server "domainB.com" -Identity $G.Name -Members $domainDNUser -Credential $creds -Verbose}
}
What are you doing?
You export to csv, but still try to save it to a variable
You search twice
You add all members from ALL groups in TEST-OU to every group in domainB
You waste time on saving and reading data that you already have in memory
You search for the user-object to get SamAccountName when you already have something ten times better, the DN. Then you use that SamAccountName to find the DN.
Try this (untested):
$creds = Get-Credential
$Groups = Get-ADGroup -Properties Members -Filter * -SearchBase "OU=TEST,OU=Shop Print Groups,OU=User,OU=domain Groups,DC=domain,DC=com"
Foreach($G In $Groups)
{
#Display group members and group name
Write-Host $G.Name
Write-Host "-------------"
$G.Members
#Add members to domainB group
$G.Members |
Get-ADUser -Server fairfieldmfg.com |
ForEach-Object { Add-ADGroupMember -Server "domainB.com" -Identity $G.Name -Members $_ -Credential $creds -Verbose }
}
I used a foreach-loop to run the Add-ADGroupMember because it usually fails in the middle of a group of members if it finds on the already is a member, but if we add them one at a time you get around that (or you could do a search and exclude those already in the group).
You may want to add -ErrorAction SilentlyContinue to Add-ADGroupMember to ignore those errors when you know the script works as it should.

If And statement with a foreach for AD Users in Powershell

$names = Import-CSV C:\PowerShell\TerminatedEmployees.csv
$Date = Get-Date
foreach ($name in $names)
{
Get-ADPrincipalGroupMembership -Identity "$($name.TextBox37)" | select Name | Out-File "C:\Powershell\ADUserMemberships\$($name.TextBox37)Memberships.txt"
$ADgroups = Get-ADPrincipalGroupMembership -Identity "$($name.TextBox37)" | where {$_.Name -ne "Domain Users"}
Remove-ADPrincipalGroupMembership -Identity "$($name.TextBox37)" -MemberOf $ADgroups -Confirm:$false
Disable-ADAccount -Identity "$($name.TextBox37)"
Get-ADUser -Identity "$($name.TextBox37)" | Move-ADObject -TargetPath "OU=DisabledAccounts,OU=XXX,DC=XXX,DC=XXXX,DC=XXX"
Set-ADUser -Identity "$($name.TextBox37)" -Description "Disabled $Date"
}
This is an already working script I have. However, I realized I need to check 2 properties on the AD user to determine if they need to need to go through my foreach statement. Both properties need to be met. If they are then there's no reason for the AD users to be processed.
The AD user is already disabled.
The AD user already resides in the Disabled OU.
I'm thinking this needs to be done in an If -And statement. But does this need to be done before the foreach or inside the foreach?
Start out by retrieving the user account with Get-ADUser and then inspect the Disabled property + compare the Disabled OU to the DistinguishedName of the user:
$names = Import-CSV C:\PowerShell\TerminatedEmployees.csv
$Date = Get-Date
$DisabledOU = "OU=DisabledAccounts,OU=XXX,DC=XXX,DC=XXXX,DC=XXX"
foreach ($name in $names)
{
$ADUser = Get-ADUser -Identity "$($name.TextBox37)"
if(-not($ADUser.Enabled) -and $ADUser.DistinguishedName -like "*,$DisabledOU")
{
# no need to proceed, skip to next name in foreach loop
continue
}
$ADGroups = Get-ADPrincipalGroupMembership -Identity "$($name.TextBox37)"
$ADGroups |Select-Object Name |Out-File "C:\Powershell\ADUserMemberships\$($name.TextBox37)Memberships.txt"
# no need to call Get-ADPrincipalGroupMembership again
$ADgroups = $ADGroups | where {$_.Name -ne "Domain Users"}
Remove-ADPrincipalGroupMembership -Identity "$($name.TextBox37)" -MemberOf $ADgroups -Confirm:$false
Disable-ADAccount -Identity "$($name.TextBox37)"
$ADUser | Move-ADObject -TargetPath $DisabledOU
Set-ADUser -Identity "$($name.TextBox37)" -Description "Disabled $Date"
}