I've been trying to find analogs to this in the forums, but it's the logic that's tying me up - putting it all together.
I have an AD and I have a CSV of users that should be in a particular OU. I want to compare the users in the OU to the CSV, and users NOT in the CSV, I want to disable them and move them to a different OU.
I'm new to Powershell and having a bit of a rough time with this. What's getting me is the comparison and IF-Then logic...I just can't get the syntax right. I've tried a few options...this is what I have right now
Import-Module ActiveDirectory
$path = "f:\aDMGMT\"
$logpath = "f:\admgmt\logs\diable_ad_users.log"
$userfile = $path + "\files\ad_currentemployees.csv"
$location = "OU=Faculty,OU=People,DC=mydomain,DC=com"
$disabledou = "OU=disabledemployees,OU=Disabled,DC=mydomain,DC=com"
$AD_users = Get-ADUser -Filter * -SearchBase "OU=Faculty,OU=People,DC=mydomain,DC=com" | select -ExpandProperty SamAccountName
$sams = $userfile | Select-Object -ExpandProperty NameUnique #the
Compare-Object $AD_users $sams | Out-File $logpath
But the tags available are things like includeequal and excludedifferent...but not includedifferent...and how would I do it for only one side?
Help!
What you could do is pipe the results into a where clause using the SideIndicator to filter on.
Compare-Object $AD_users $sams |
Where-Object{$_.SideIndicator -eq "<="} |
Select-Object -expandproperty inputobject
Using the direction that you need, either "<=" or "=>", you would then pipe into a Select-Object to restore the input object that you were filtering on. That last part would be more important if your object was multidimensional.
I am going to try this with real data as this has only breifly tested but should work.
I re-thought my logic and came up with this. It works perfectly.
Import-Module ActiveDirectory -ErrorAction Stop
$path = "f:\aDMGMT\"
$date = Get-Date
$logdate = Get-Date -UFormat "%y%m%d"
$log = $path+"\logs\diable_ad_users_"+$logdate+".log"
$userfile = $path + "\files\ad_currentemployees.csv"
$location = "OU=employees,OU=People,DC=myorg,DC=com"
$disabledou = "OU=disabledemployees,OU=Disabled,DC=myorg,DC=com"
$AD_users = Get-ADUser -Filter * -SearchBase "OU=employees,OU=People,DC=myorg,DC=com" | select -ExpandProperty SamAccountName
$sams = Import-csv $userfile | select nameunique
ForEach ($user in $AD_users)
{
$exists = $sams.nameunique -contains $user # clean output for array w/ header vs. array without header demands .namunique
If(!$exists)
{
Get-ADUser -Identity $user | Move-ADObject -targetpath $disabledou
Disable-ADAccount -Identity $user
}
}
Related
So basically, I have a script that can generate a list of all the users in Active Directory, with their lastlogondate, samaccountname and name. However, I want to add a new clause to this where it EXCLUDES disabled (users). There doesn't seem to be an object such as disabledUsers that I could just add to the end of my Where-Object part.
So because there is no object ID for disabled users, I understand that it should be something like this:
$userList = Get-ADuser -filter {Enabled -eq $True}
But I keep running in to syntax errors when I add this
$companyName = "GG"
$dateStr = (Get-Date).ToString("yyyyMMdd-hhmmss")
$exportPath = "C:\"
$exportFileName = "$($exportPath)\($dateStr)_($companyName)_userlist.csv"
$userList = Get-ADUser -Filter "*" | Where-Object {($_.DistinguishedName -notLike "*System*")} | Select-Object Name,samAccountName, lastLogonDate
$userList | Export-Csv -Path $exportFilename -NoTypeInformation
I would very much appreciate some assistance here friends.
I am trying to grab the host file entries of servers in mulptiple OUs here to show the host file entries and server names
$OUpath =
'OU=Sales,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=DCHR,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=Finance,OU=Servers,OU=Test,OU=Upgraded,DC=fabrikam,DC=local'
$ExportPath = 'c:\servers.csv'
$OUpath | Foreach {
Get-ADComputer -Filter * -SearchBase $OUpath} | Select-object DistinguishedName,DNSHostName,Name,Description | Export-Csv -NoType $ExportPath
Part A up ran fine...How can i get the entries of the results. I am tending towards content but hope to have it all in one script. Any help would be nice.
An alternative to #FoxDeploy's helpful answer, here is how you can do the same using the pipelines with ForEach-Object.
Note that Description is not a default property for Get-ADComputer you will need to add -Properties Description to see it's value.
Another point to consider, by default, if you don't specify the -SearchScope, Get-ADComputer will perform a SubTree search, meaning that it will bring all computers of the specified OU and all computers on all the OUs contained in the Base OU. If you just want to bring the computers in the OU without going down in recursion, you should add -SearchScope OneLevel.
#(
'OU=Sales,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=DCHR,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=Finance,OU=Servers,OU=Test,OU=Upgraded,DC=fabrikam,DC=local'
) | ForEach-Object {
Get-ADComputer -Filter * -SearchBase $_ -Properties Description
} | Select-Object DistinguishedName,DNSHostName,Name,Description |
Export-Csv 'c:\servers.csv' -NoTypeInformation
I think the primary issues were the array getting declared incorrectly, and incorrect syntax for the ForEach-Object cmdlet
$OUpath = #(
'OU=Sales,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=DCHR,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=Finance,OU=Servers,OU=Test,OU=Upgraded,DC=fabrikam,DC=local'
)
$ExportPath = 'c:\servers.csv'
$OUpath |
ForEach-Object {
Get-ADComputer -Filter * -SearchBase $_ -Properties Description
} |
Select-Object DistinguishedName, DNSHostName, Name, Description |
Export-Csv $ExportPath -NoTypeInformation
You have to use $_ in this context where you were using $OUpath previously. Select-Object can take the the piped output from the ForEach-Object loop rather than being in the loop, which should be more efficient. Likewise for Export-Csv.
As implied by FoxDeply's very good answer that might signal an attempt to use A ForEach(...) loop construct instead of ForEach-Object. But if we are going that route I think it's slightly better to let PowerShell populate the array for us.
$OUpath = #(
'OU=Sales,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=DCHR,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local'
'OU=Finance,OU=Servers,OU=Test,OU=Upgraded,DC=fabrikam,DC=local'
)
$Servers =
ForEach( $Path in $OUpath )
{
Get-ADComputer -Filter * -SearchBase $path -Properties Description |
Select-Object DistinguishedName, DNSHostName, Name, Description
}
$Servers | Export-Csv $ExportPath -NoTypeInformation
Alternatively you could skip the Select-Object inside the loop and add $Servers = $Servers | Select-Object ... right after the loop. Although the difference is probably negligible.
With some minor restructuring, this should get you past your issue
$OUpath = (
'OU=Sales,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local',
'OU=DCHR,OU=Servers,OU=_Production,OU=Upgraded,DC=fabrikam,DC=local',
'OU=Finance,OU=Servers,OU=Test,OU=Upgraded,DC=fabrikam,DC=local')
$ExportPath = 'c:\servers.csv'
$servers = new-object System.Collections.ArrayList
ForEach($path in $OUpath){
$ouServers = Get-ADComputer -Filter * -SearchBase $path | Select-object DistinguishedName,DNSHostName,Name,Description
$servers.AddRange($ouServers) | Out-Null
}
"found $($servers.Count) servers!"
$servers | export-csv $exportPath
I made the list of OU Paths a PowerShell array, then iterate through them using the standalone ForEach loop. Then commit the items to a variable that will persist ($servers) and output the CSV.
I have a fairly simple script that needs to check around 20,000 AD Groups for their membership count. That all works fine, I can take the list of groups run it through the script and for the most entries it works fine. However I was getting some errors that I couldn't figure out and hopefully someone here can point me in the right direction.
I am using the DN of the object to query AD and for around 10% it fails, but when I copy the DN from the file, paste it into a command window and run the command manually it works fine. Some more checking and it seems that when I read an offending line into my variable there is a line break in the middle for some reason.
When looking at the value of the variable I get the following:
Working Example - "CN=ABC, OU=Location, OU=Distribution Lists, DC=Domain, DC=COM"
Error Example - "CN=ABC, OU=Location, OU=Distribution
Lists, DC=Domain, DC=COM"
It seems to insert a return in-between Distribution and Lists on certain entries in the file. I have tried deleting the character in-between and replacing it with a space but I get the same result.
Could it be the length? I am still looking for a common factor but any suggestions would be great.
Thanks
Updated with requested content.
$Groups = Import-Csv C:\Temp\DLName.csv
write-host ($Groups).Count
$i=1
foreach ($Group in $Groups)
{
$GroupInfo = Get-ADGroupMembersRecursive -Groups $Group.Name
$MembersCount = ($GroupInfo | Measure-Object).Count
$MembersList = $GroupInfo | Select Name -ExcludeProperty Name
$FriendlyName = Get-ADGroup -Identity $Group.Name
$Export = $FriendlyName.Name + ", " + $MembersCount
$Export | Out-File C:\Temp\DLMembers.csv -Append
Write-host $FriendlyName "," $MembersCount
$i
$i++
}
Entry 1 and 3 work 2 doesn't, but the formatting here seems to have wrapped the entries.
Name
"CN=Company - DL Name1,OU=Country1 Distribution Lists,OU=Europe,OU=Acc,DC=Domain,DC=Domain,DC=com"
"CN=Company - DL Name2,OU=Country2 Distribution Lists,OU=Europe,OU=Acc,DC=Domain,DC=Domain,DC=com"
"CN=Company - DL Name3,OU=Country3 Distribution Lists,OU=America,OU=Acc,DC=Domain,DC=Domain,DC=com"
Top pic is the failure second pic works.
List Creation:
$SearchScope = "OU=OUName,DC=Domain,DC=Domain,DC=com"
$SearchFilter = {GroupCategory -eq 'Distribution'}
$Groups = Get-ADGroup -SearchBase $SearchScope -Filter
$SearchFilter | Sort-Object Name
foreach ($Group in $Groups)
{
$Group.DistinguishedName | Select Name -ExpandProperty Name
$Group.DistinguishedName | Out-File C:\Temp\DLName.csv -Append
}
Do not use a self-combined comma separated string and Out-File to create CSV files, because that will get you into trouble when fields happen to contain the delimiter character like in this case the comma (which will lead to mis-aligned data).
Your List Creation code should be like this:
$SearchBase = "OU=OUName,DC=Domain,DC=Domain,DC=com"
$SearchFilter = "GroupCategory -eq 'Distribution'"
Get-ADGroup -SearchBase $SearchBase -Filter $SearchFilter |
Sort-Object Name | Select-Object Name, DistinguishedName |
Export-Csv -Path 'C:\Temp\DLName.csv' -NoTypeInformation
Then you can use that csv later to do:
$Groups = Import-Csv -Path 'C:\Temp\DLName.csv'
Write-Host $Groups.Count
$result = foreach ($Group in $Groups) {
$GroupInfo = Get-ADGroupMember -Identity $Group.DistinguishedName -Recursive
# unnecessary.. $MembersCount = ($GroupInfo | Measure-Object).Count
# unused.. $MembersList = $GroupInfo.Name
# unnecessary.. $FriendlyName = Get-ADGroup -Identity $Group.Name
# output an object with the wanted properties
[PsCustomObject]#{
GroupName = $Group.Name
MemberCount = #($GroupInfo).Count # #() in case there is only one member in the group
}
}
# show on screen
$result | Format-Table -AutoSize
# output to CSV file
$result | Export-Csv -Path 'C:\Temp\DLMembers.csv' -NoTypeInformation
As you can see, I'm not using your custom function Get-ADGroupMembersRecursive because I have no idea what that outputs.. Also, there is no need for that because you can use the Get-ADGroupMember cmdlet with the -Recursive switch added
Good Afternoon
I am trying to create a PS script which pulls all users not in a certain Security group. I have managed to get this to work fine. However i require it to omit certain OU's as i don't want certain accounts included in this process like terminated users and support accounts for examples.
So i created the below to do this but it seems to fail. Its where i have tried to add some filtering. Can someone help put this in the right direction?
import-Module activedirectory
$results = #()
$users = Get-ADUser -Properties memberof -Filter {enabled -eq $true} | ? {$_.DistinguishedName -notlike "*,OU=Exchange,OU=Support Accounts,OU=Terminated Users and Computers do not use,OU=TerminatedEmployeesContractors,OU=TestAccounts*"} *
$ExportPath = 'c:\app\users_in_ou1.csv'
foreach ($user in $users) {
$groups = $user.memberof -join ';'
$results += New-Object psObject -Property #{'User'=$user.name;'Groups'= $groups}
}
$results | Where-Object { $_.groups -notmatch 'SG_XXXXXXXXXXX' } | Select-Object user | export-csv $ExportPath
Thanks
I would build a regex from all OUs that should be excluded from the search by joining the strings with the regex 'OR' character (|) and use the -notmatch operator.
Because there may be characters in these strings that have special meaning in regex, use [Regex]::Escape() on each before joining them.
Something like below:
Import-Module ActiveDirectory
# create a regex from an array of OUs to exclude by 'OR-ing' them with the pipe character
$excludeOUs = ('OU=Exchange','OU=Support Accounts','OU=Terminated Users and Computers do not use',
'OU=TerminatedEmployeesContractors','OU=TestAccounts' | ForEach-Object {[Regex]::Escape($_)}) -join '|'
$ExportPath = 'c:\app\users_in_ou1.csv'
# get a list of objects not having any of the excluded OUs in their DistinguishedName
# and at the same time output objects with properties 'User' and 'Groups'
$users = Get-ADUser -Properties Name, MemberOf -Filter 'Enabled -eq $true' |
Where-Object {$_.DistinguishedName -notmatch $excludeOUs} |
Select-Object #{Name = 'User'; Expression = {$_.Name}},
#{Name = 'Groups'; Expression = {($_.MemberOf -join ';')}}
# next filter this out further by excluding a certain group and export to Csv
$users | Where-Object { $_.Groups -notmatch 'SG_XXXXXXXXXXX' } | Export-Csv $ExportPath -NoTypeInformation
I've been trying to work through this for quite some time. My ultimate goal is to get the exported report as a single csv sheet. However, I've been highly unsuccessful. I then broke it down to export 2 sheets that I can just merge, however, CIM is not playing nice with that at all. Then my other issue came with not calling from my list properly.
$ComputerList = "C:\ps_test\pclastlogon.txt"
$LogPath = "C:\ps_test\Logs"
$LogTime = Get-Date -Format s | foreach {$_ -replace ":", "-"}
$CsvLogonPath = $LogPath+'\RebootStatus-'+$LogTime+'-Logon.csv'
$CsvBootPath = $LogPath+'\RebootStatus-'+$LogTime+'-LastBoot.csv'
Import-Module ActiveDirectory
IF ( -Not (Test-Path -Path $LogPath)) {New-Item -Path $LogPath -ItemType Directory}
$Computers = Get-Content $ComputerList
Foreach ($Computers in $ComputerList) {
Get-ADComputer -Identity $Computers -Properties * -Filter * | Select-Object cn,LastLogonDate,#{LABEL="Last Logon";EXPRESSION={[DateTime]::FromFileTime($_.LastLogon)}} | Export-Csv -Path $CsvLogonPath
}
Foreach ($Computers in $ComputerList) {
Get-CimInstance Win32_OperatingSystem -ComputerName $Computers | Select csname,LastBootUpTime | Export-Csv -Path $CsvBootPath
}
Can someone please point me in the right direction? Thanks in advance.
Not to use -filter * -Properties *, its too expensive. Mention the required Properties in -Properties and if you are mentioning -Identity, -filter * is not necessarily required.
Wrap Get-ADComputer and Get-CimInstance in a single foreach and create a CustomObject then export to CSV.
[Not Tested]
Fore example:
$AllDetails = Foreach ($Computers in $ComputerList) {
$DetailsfromAD = Get-ADComputer -Identity $Computers -Properties cn,LastLogonDate,LastLogon | Select-Object cn,LastLogonDate,#{LABEL="Last Logon";EXPRESSION={[DateTime]::FromFileTime($_.LastLogon)}}
$DetailsFromCIM = Get-CimInstance Win32_OperatingSystem -ComputerName $Computers | Select csname,LastBootUpTime
$PropertyHash = #{
CN = $DetailsfromAD.CN
LastLogonDate = $DetailsfromAD.LastLogonDate
'Last Logon' = $DetailsfromAD.'Last Logon'
csname = $DetailsFromCIM.csname
LastBootUpTime = $DetailsFromCIM.LastBootUpTime
}
New-Object -TypeName PSObject -Property $PropertyHash
}
Export $AllDetails to a CSV file
Just a guess here but after piping I think you need to for-each your command list. Something like
Get-ADComputer -Identity $Computers -Properties * -Filter * | % { Select-Object cn,LastLogonDate,#{LABEL="Last Logon";EXPRESSION={[DateTime]::FromFileTime($_.LastLogon)}} | Export-Csv -Path $CsvLogonPath }
But then you will need to do something to append each result instead of just having the last one in $CSvLogonPath
A general approach to join two object lists that result from a single source list:
Add the computer name ($Computers) from the original computer list ($ComputerList) as a primary key in both object lists using #{Label="ComputerName"; Expression={$Computers}}:
$ADComputers = Foreach ($Computers in $ComputerList) {
Get-ADComputer -Identity $Computers -Properties * -Filter * | Select-Object #{Label="ComputerName"; Expression={$Computers}},cn,LastLogonDate,#{LABEL="Last Logon";EXPRESSION={[DateTime]::FromFileTime($_.LastLogon)}}
}
$CimInstances = Foreach ($Computers in $ComputerList) {
Get-CimInstance Win32_OperatingSystem -ComputerName $Computers | Select #{Label="ComputerName"; Expression={$Computers}},csname,LastBootUpTime
}
Use the Join-Object function to join the object lists on the ComputerName:
$ADComputers | Join $CimInstances -On ComputerName | Export-Csv -Path $CsvBootPath
You might consider to go easy on this and forget about the primary key and just join the two tables based on the their index:
$ADComputers | Join $CimInstances -Using {$LeftIndex -eq $RightIndex}
But I recommend against this because if one of your tables is missing a record (e.g. because it doesn't exist the database), the indexes will likely be incorrect aligned.