Output department and direct reports - powershell

I am trying to create an "initial" text file that will hold a script run of all users + department + direct reports. My next step after making this file is to create another file the same way but compare it to the original to see if the department for the users ever changed. (not sure yet how to compare the department value just yet)
My current issue is that the department, even though the process is identical to another program I have made in the past, won't print it. Furthermore, when it prints my direct reports it prints only the first one with the whole extension of CN=..., OU=... etc.
I want it to print this way:
username | Department(extensionAttribute14) | Direct Reports (as a single string)
we38432 | IT-Security | cm03456: 04555a: ....etc
My original script used this code for department:
$deps = Get-Aduser -filter {name -like *} -Properties name, extensionAttribute14 | Select name, extensionAttribute14 | Export-CSV $listing -notypeinformation
and this worked. I tried the {name -like *} but that gave me errors in my current program. I know the Export-CSV makes it work but I can't use this format anymore.
for the direct reports my original was this:
foreach ($ID in $directReports){
if ($ID -ne $Null){
$directreports = get-aduser $ID
$directreports.name | Out-File $output -Append
}
This code printed line by line the direct reports but I want them all listed in the same excel cell when I send it there.
I have printed a listing of all the members in the past using ":" and it worked but it is not the case with the direct reports listing. I just get errors when I use this format from my other program:
foreach ($member in $empty.members){
$string = $member.substring(3,$member.indexof(",")-3)
$members = $members + ":" + $string
}
I hope someone can help me with my two issues.
Import-Module ActiveDirectory
$documentOld = "C:\Temp\Old_Supervisor_list_mo_yyyy.txt"
Clear-Content $documentOld
$Header = `
"User ID" <#+ "|" + `
"Department" + "|" + `
"Direct Reports"#>
$Header | Out-File $documentOld -Append
$Users = Get-AdUser -Filter * -Properties name, Enabled, Manager, extensionAttribute14 | Select Enabled, name, Manager, extensionAttribute14
foreach ($user in $Users){
if ($user.enabled –eq $true) {
$name = $user.name
$directReports = Get-ADUser -Identity $name -Properties directreports | Select -ExpandProperty directreports
$department = $user.extensionAttribute14
foreach ($ID in $directReports){
if ($ID -ne $Null){
$directreports = get-aduser $ID
# $string = $directreports + ":"
}#end if $ID
}#end foreach $ID
$listing = `
$name + "|" + $deparment + "|" + $directreports#$string
$listing | Out-File $documentOld -Append
}# end if
}# end foreach $user

Let see if we can make this a little easier and efficient.
Import-Module ActiveDirectory
$documentOld = "C:\Temp\Old_Supervisor_list_mo_yyyy.txt"
$Users = Get-AdUser -Filter * -Properties name,Enabled,Manager,extensionAttribute14 | Where-Object{$_.Enabled}
$Users | ForEach-Object{
$props = #{
Name = $_.Name
Department = $_.extensionAttribute14
DirectReports = ($_.Manager | Where-Object{$_} | ForEach-Object{Get-Aduser $_ | Select-object -ExpandProperty Name}) -join ":"
}
New-Object -TypeName psobject -Property $props
} | Select-Object Name,Department,DirectReports | Export-CSV -Delimiter "|" -NoTypeInformation -Path $documentOld
First we get all the users from your directory with Get-AdUser -Filter * taking all the properties outside the norm that we want. Since you just wanted accounts that are enabled we filter those out now with Where-Object{$_.Enabled}.
The fun part is creating the custom object array ( which is necessary for input for Export-CSV). Create a small hashtable called $props where we set the properties by their friendly names. The special one being DirectReports where we take all the users manager DN's ( Assuming they have one where is what Where-Object{$_} does by filtering out nulls/empty strings.) and use Get-Aduser to get there names. Since you could have more than one manager an array is most likely returned we use -join to ensure only a single string is given for the DirectReports property. That property collection is created for every user and it is then used to create a New-Object which is sent to the output stream.
The Select-Object that follows is just to ensure the order of columns in the CSV that is created. No need for making a CSV file with lots of Out-Files when Export-CSV and -Delimiter "|" will do the hard work for you.

Related

Renaming computer based using Powershell and CSV

I'm trying to create a Powershell script that creates a CSV from a specific OU, takes the last created computer (ie computer-200), adds 1 (so computer-201) and renames the computer. I was able to create the CSV but haven't been able to add an increment of 1 to the name.
Here is the script so far:
Add-WindowsCapability –online –Name “Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0”
$OUpath = 'OU=Computers,OU=Test-Devices,DC=Test,DC=local'
$ExportPath = 'c:\temp\computers_in_ou.csv'
Get-ADComputer -Filter * -SearchBase $OUpath -Properties whenCreated | select-object Name,whenCreated | sort whenCreated | Export-Csv -NoType $ExportPath
$csvdata = Import-Csv 'c:\temp\computers_in_ou.csv'
$csvdata | Select-Object Name -Last 1 | Export-Csv -NoType 'c:\temp\renameWS.csv'
$name = Import-Csv 'c:\temp\renameWS.csv' | Select-Object Name -Last 1
The $name shows output of
Name: Computer-200
How can I take that 200 and add 1?
Thank you!
You can use replacing Regex.Replace with a script block to increment the digits in the computer's name by 1:
For example:
[regex]::Replace('computer-200', '\d+', {
param($s)
[int] $n = $s.Value; (++ $n)
})
# Results in: `computer-201`
If you have access to PowerShell Core, Replacement with a script block was added in PowerShell 6 and later:
'computer-200' -replace '\d+', {
$n = [int] $_.Value; (++ $n)
}
Following above examples, you could do the following to get the latest computer name and increment the digits by 1:
$computers = Get-ADComputer -Filter * -SearchBase $OUpath -Properties whenCreated |
Select-Object Name, whenCreated | Sort-Object whenCreated
[regex]::Replace($computers[-1].Name, '\d+', {
param($s)
[int] $n = $s.Value; (++ $n)
})
It really depends on the exact format/trustworthiness of your CSV data, but here's a non-regex way to accomplish this using your existing code.
$csvdata = Import-Csv 'c:\temp\renameWS.csv'
$split = $csvdata[-1].name.Split('-')
$addOne = [int]$split[1] + 1
$final = $split[0] + '-' + $addOne
You can then take that $final string output and append to your CSV, rename with other cmdlets, etc.

Importing CSV splits certain lines

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

Get-AdUser no acepting variable

im trying to pull out all the information regarding my domain admin adminsitrators
#set domains we are going to query
$domains = 'mydomainname.com'
#first here i bring the sam accounts names
Foreach ($domain in $domains)
{
$OUTPUT =Get-AdGroupMember -identity “Domain Admins” -recursive -server $domain |
Select-Object -Property samAccountName|
Select samAccountName;
$Outputs +=$OUTPUT;
$OUTPUT |Export-CSV "C:\File\$($domain).csv" -NoTypeInformation ;
}
$OUTPUT #this print the sam accounts
#here is the problem
Foreach ($user in $OUTPUT)
{
$Users2 =Get-ADUser -Filter "SamAccountName -like '$OUTPUT'" -Properties *
$USER3 +=$Users2;
$Users2 |Export-CSV "C:\File\$($domain)Userpop.csv" -NoTypeInformation ;
}
I think this is a problem with your filter. Try changing that line as follows:
Get-ADUser -Filter " SamAccountName -like `"$($user.samaccountname)`" " -Properties *
It can't be $OUTPUT as that's an array. Your loop variable is $user which is an object so you need the .samaccountname property. The filter needs a string with the -like matching a quoted string, so you need `" to pass the quote through to the final string.
Your CSV output of $user2 may not be what you expect either as each object is output to the same file. Perhaps you mean to have a -Append or write them to different files?
You'll probably want to reset $user3 as well. Perhaps add $user3 = #() before that loop.
You should always try to avoid adding to arrays with $array += $something, because that means the entire array gets rebuild in memory, costing time and resources.
Also, I would advise using more descriptive variable names, so the code will still be understandable after some time.
Then, because you are getting info from different domains, it is important to store the domain name in the first loop together with the samaccount names, so you can use these as -Server parameter in the second loop on Get-ADUser
Try
#set domains we are going to query
$domains = #('mydomainname.com') # since there is only one domain listed, use #() to force it into an array
$domainsAndAdmins = foreach ($domain in $domains) {
# store the SamAccountNames for this domain as objects in an array
$admins = Get-AdGroupMember -Identity 'Domain Admins' -Recursive -Server $domain |
Select-Object -Property SamAccountName
# export this to csv file
$outFile = 'C:\File\{0}.csv' -f $domain
$admins | Export-Csv $outFile -NoTypeInformation
# output an object with both the domain and the array of SamAccountNames
# this will be captured in variable $domainsAndAdmins
[PsCustomObject]#{Domain = $domain; Admins = $admins.SamAccountName }
}
# output on screen
$domainsAndAdmins | Format-Table -AutoSize
# will result in something like
#
# Domain Admins
# ------ ------
# mydomainname.com {jdoe, jbloggs, mpimentel}
# myseconddomainname.com {jdoe, mpimentel}
# next get ALL (?) properties from the users we found
$domainsAndAdmins | ForEach-Object {
$domain = $_.Domain
$result = foreach ($user in $_.Admins) {
Get-ADUser -Filter "SamAccountName -like '$user'" -Server $domain -Properties * -ErrorAction SilentlyContinue
}
$outFile = 'C:\File\{0}_Userpop.csv' -f $domain
$result | Export-Csv $outFile -NoTypeInformation
}

Foreach loop and writing the results to a file

I'm working on a script which iterates through all users found across a domain, grabs a few credentials and then returns them in the format of an SQL INSERT statement which I want stored in a .txt file as output.
So far I've only been able to write the last user to a file however I'm able to print out in the terminal every single user. I have a feeling that I'm overwriting the .txt output file each time I iterate through my foreach loop.
Below is my code which has been sanitised:
$users = Get-ADUser -Properties uidNumber, sAMAccountName -SearchBase' OU=LiveUsers,OU=Users,OU=MyBusiness,DC=local' -Filter *
$message = ""
Set-Content -Path C:\Desktop\UIDs\currentList.txt -Value $null # ensures file is blank
foreach ($user in $users | Select-Object -Property uidNumber, sAMAccountName){
#Search in specified OU and List above for UID and name and write to a file
$message = "INSERT INTO `DataBaseNameHere`.`currentUser` (`User_id`, `User_name`) VALUES ('" + $user.uidNumber + "', '" + $user.sAMAccountName + "');" |
Out-File -FilePath C:\Desktop\UIDs\currentList.txt
}
Get-Content -Path C:\Desktop\UIDs\currentList.txt
I've tried other variations of foreach loops, Out-File and Tee-Object so far.
Assuming that the sanitized code you provided does what you want except for leaving only a single line in the output file, you need to ensure that you have either no existing output file or that it's blank, and then you add the -append switch to the Out-File cmdlet:
$users = Get-ADUser -Properties uidNumber, sAMAccountName -SearchBase 'OU=LiveUsers,OU=Users,OU=MyBusiness,DC=local' -Filter *
Set-Content -Path C:\Desktop\UIDs\CurrentList.txt -Value $null # ensures file is blank
foreach ($user in $users | Select-Object -Property uidNumber, sAMAccountName) {
$message = "INSERT INTO `databaseNameHere`.`currentUser` (`User_id`, `User_name`) VALUES ('" + $user.uidNumber + "', '" + $user.sAMAccountName + "');"
Out-File -FilePath C:\Desktop\UIDs\currentList.txt -append # -append added to not overwrite existing content.
}
See Out-File at Microsoft Docs.
The code you posted would not write anything to a file since the loop defines $message without doing anything with it, and then calls Out-File without any input.
Something like this should do what you want:
Get-ADUser ... |
Select-Object uidNumber, sAMAccountName |
ForEach-Object { "INSERT INTO `databaseNameHere`.`currentUser` (...);" } |
Out-File -FilePath C:\Desktop\UIDs\currentList.txt
Beware though, that building INSERT statements that way is vulnerable to SQL injection and should be avoided.
So, after taking elements from a few of your answers I was able to modify my code and fix the loop. I was just using the Out-File cmdlet wrong.
$users = Get-ADUser -Properties uidNumber, sAMAccountName -SearchBase 'OU=LiveUsers,OU=Users,OU=MyBusiness,DC=myCompany,DC=local' -filter *
Clear-Content -Path C:\Desktop\UIDs\CurrentList.txt
foreach ($user in $users ){
“INSERT INTO `databasename`.`currentUser` (`User_id`, `User_name`) VALUES ('"+ $user.uidNumber + "','"+ $user.sAMAccountName +"');" |
Out-File -FilePath C:\Desktop\UIDs\CurrentList.txt -Append
}

User & Group Audit Script

I am looking for help writing a powershell script that will query Active Directory and output a CSV.
This script will list all groups and all users and signify with a character when a user belongs to that group.
The output will look like this: https://imgur.com/1MfFv7Q
I've tried using dsquery and various other powershell methods, but none seem to work.
I'm hoping someone here will have a different perspective on this and be able to help out.
Thank you!
Update 1:
As requested, here's my code that I was trying to work with previously.
#Get a list of the groups
$groups = Get-ADGroup -filter * -Properties Name | Select Name
#iterate through groups array and append each with a comma
$output = ForEach ($g in $groups){
$topgroups.Add($g)
$topgroups.Add(",")
}
#for each group, find out if the user is part of that group
$output = ForEach ($g in $groups) {
$results = Get-ADGroupMember -Identity $g.name -Recursive | Get-ADUser -Properties enabled, SamAccountName, givenname, surname,physicalDeliveryOfficeName
ForEach ($r in $results){
New-Object PSObject -Property #{
GroupName = $g.Name
Username = $r.name
DisplayName = $r.displayname
}
}
}
$output | Export-Csv -path c:\temp\output.csv -NoTypeInformation
Update 2:
Added FTP Upload and some more information. Thanks again TheMadTechnician!
My goal is to get this information from each of my clients, import this into SQL with SSIS with a timestamp, and then I can do can do comparison through sql reporting.
Here's my script where it is currently:
New-Item c:\temp\audit -type directory
$Domain = (gwmi WIN32_ComputerSystem).Domain
$filename = $Domain + "_ADExport.csv"
$fileoutput = "c:\temp\audit\" + $filename
Remove-Item $fileoutput
$GroupRef = #{}
Get-ADGroup -filter * | ForEach{$GroupRef.Add($_.DistinguishedName,$_.Name)}
$Users = Get-ADUser -Filter * -Prop MemberOf, passwordlastset, LastLogonDate
ForEach($User in $Users){
$LineItem = [PSCustomObject]#{'Enabled'=$User.Enabled;'First Name'=$User.givenname;'Last Name'=$User.surname;'Location'=$User.physicalDeliveryOfficeName;'Domain'=$Domain;'SAMAccountName'=$User.samaccountname;'LastLoggedOn'=$User.lastlogonDate;'PasswordLastSet'=$User.passwordlastset}
$GroupRef.Values | ForEach{Add-Member -InputObject $LineItem -NotePropertyName $_ -NotePropertyValue ""}
$User.MemberOf | ForEach{$LineItem.$($GroupRef["$_"]) = "X"}
[Array]$Results += $LineItem
}
$Results|export-csv $fileoutput -notype
#we specify the directory where all files that we want to upload
$Dir="C:/temp/audit/"
#ftp server
$ftp = "ftp://8.8.8.8/"
$user = "test"
$pass = "ThisIsARea11yL0NgPa33Word"
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
#list every file
foreach($item in (dir $Dir "*.csv")){
"Uploading $item..."
$uri = New-Object System.Uri($ftp+$item.Name)
$webclient.UploadFile($uri, $item.FullName)
}
Update 3:
Good afternoon:
I've run into an issue where I am trying to restrict which OU this searches through:
$GroupRef = #{}
$OUPATH = (Get-ADOrganizationalUnit -Filter 'Name -like "CLIENT_GROUPS"' | FT DistinguishedName -HideTableHeaders | Out-String).Trim()
Get-ADGroup -SearchBase "$OUPATH" -Filter * | ForEach{$GroupRef.Add($_.DistinguishedName,$_.Name)}
The error is:
Exception setting "": "Cannot process argument because the value of argument "name" is not valid. Change the value of
the "name" argument and run the operation again."
At C:\Users\f12admin\Desktop\test.ps1:23 char:42
+ $User.MemberOf | ForEach{$LineItem.$($GroupRef["$_"]) = "X"}
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
All you need are Get-ADUser, Get-ADGroup, New-Object, Add-Member, and Export-CSV. I'd build a hashtable of groups linking their distinguishedname and their displayname. Then I'd get a list of all users, create a custom object for each user, loop through the list of groups and add a property to the custom object for each group. Then loop through the user's MemberOf property and set the associated property on the custom object to "X" for everything there. Collect all of the custom objects in an array, and export it to a csv.
This isn't tested, but here's the theory...
$GroupRef = #{}
Get-ADGroup -filter * | ForEach{$GroupRef.Add($_.DistinguishedName,$_.Name)}
$Users = Get-ADUser -Filter * -Prop MemberOf
ForEach($User in $Users){
$LineItem = [PSCustomObject]#{'DisplayName'=$User.DisplayName;'SAMAccountName'=$User.samaccountname}
$GroupRef.Values | ForEach{Add-Member -InputObject $LineItem -NotePropertyName $_ -NotePropertyValue ""}
$User.MemberOf | ForEach{$LineItem.$($GroupRef["$_"]) = "X"}
[Array]$Results += $LineItem
}
$Results|export-csv c:\temp\output.csv -notype