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
}
}
Related
I can get the user accounts in the group with the following script. But I want to get both computer object and user object in some groups. how can I do it?
Import-Module ActiveDirectory
$groups = Get-ADGroup -Filter "Name -like 'TST*'"
ForEach ($Group in $Groups) {
Get-ADGroupMember -Identity $group |
Get-ADUser -Properties samaccountname,mail,AccountExpires,manager,employeeid,employeetype |
Select-Object samaccountname,mail,employeeid,employeetype,#{l="expiration_date";e={ accountExpiresToString($_.AccountExpires)}},#{n=”Manager Name”;e={(Get-ADuser -identity $_.Manager -properties displayname).DisplayName}} |
Export-CSV -Path "C:tmp\$($group.Name).csv" -NoTypeInformation
}
Just test for the objectclass and deal with accordingly. Use a custom object to construct the output.
Import-Module ActiveDirectory
$groups = Get-ADGroup -Filter "Name -like 'TST*'"
$groups | ForEach-Object {
$Results = Get-ADGroupMember -Identity $_ | ForEach-Object {
#
# For each objectclass you must output the same properties else
# the custom PS object will not construct across object types
# I find this the best way to make it obvious what your code is doing:
#
If ($_.objectclass -eq 'user') {
$ObjDetails = Get-ADUser -Identity $_ -Properties mail,AccountExpires,manager,employeeid,employeetype
$Mail = $ObjDetails.mail
$EmployeeID = $ObjDetails.employeeid
$EmployeeType = $ObjDetails.employeetype
# Unless you're SURE all these fields are populated correctly use Try...Catch or test for the field before assigning
Try {
$ManagerName = (Get-ADuser -identity $ObjDetails.Manager -properties displayname -ErrorAction Stop).DisplayName
}
Catch {
$ManagerName = 'Not found'
}
# Not in a User object, but you must define empty fields
$DNSHostName = ''
}
If ($_.objectclass -eq 'computer') {
$ObjDetails = Get-ADComputer -Identity $_ -Properties DNSHostName
# These fields are not in a Computer object but you must define them
$Mail = ''
$EmployeeID = ''
$EmployeeType = ''
$ManagerName = ''
# This field unique to computer object
$DNSHostName = $ObjDetails.DNSHostName
}
[pscustomobject]#{
SamAccountName = $ObjDetails.SamAccountName
Mail = $Mail
EmployeeID = $EmployeeID
EmployeeType = $EmployeeType
ManagerName = $ManagerName
DNSHostName = $DNSHostName
}
}
# You can then display/export results object
$Results # | Export-CSV -Path "C:\tmp\$($group.Name).csv" -NoTypeInformation
}
The below PowerShell script iterates through the groups listed in the test.csv file.
It pulls samAccountName and distinguishedName from each user in the various groups. However, when I try to pull groupName the output is "Microsoft.ActiveDirectory.Management.ADPropertyValueCollection". Not sure how to fix this-
$groups = Get-Content test.csv
$domains = (Get-ADForest).Domains
foreach ($group in $groups)
{
foreach ($domain in $domains)
{
if (Get-ADGroup $group -Server $domain)
{
Get-ADGroupMember $group -Server $domain | select groupName, samAccountName, distinguishedName | Export-Csv c:\temp\file.csv -notypeinformation -append
}
}
}
I have tried the below, but it just outputs an empty column instead:
$groups = Get-Content test.csv
$domains = (Get-ADForest).Domains
foreach ($group in $groups)
{
foreach ($domain in $domains)
{
if (Get-ADGroup $group -Server $domain)
{
Get-ADGroupMember $group -Server $domain | select samAccountName, distinguishedName |
Get-ADUser #{name = ‘MemberOf’;’expression={$_.MemberOf -join “;”}} |
Export-Csv c:\temp\file.csv -notypeinformation
}
}
}
Try this, I added some optimization to your code :)
Note, try {...} catch {...} is more or less needed here because both cmdlets Get-ADGroup and Get-ADGroupMember will throw if the object is not found.
$ErrorActionPreference = 'Stop'
$groups = Get-Content test.csv
$domains = (Get-ADForest).Domains
$result = foreach ($group in $groups)
{
foreach ($domain in $domains)
{
try
{
$adGroup = Get-ADGroup $group -Server $domain
$members = (Get-ADGroupMember $adGroup -Server $domain).where({
# Here, I assume your looking only for user objects
# since you're next cmdlet is Get-ADUser
$_.ObjectClass -eq 'user'
})
foreach($user in $members)
{
# !!! $adUser = Get-ADUser $user >> This is not needed,
# Get-ADGroupMember already brings you the samAccountName of
# each object
# Here you can cast the result, I assume
# you want your CSV to have the Name of the Group and
# the samAccountName and distinguishedName of each user
[pscustomobject]#{
GroupName = $adGroup.Name
samAccountName = $user.samAccountName
distinguishedName = $user.distinguishedName
}
}
}
catch
{
Write-Warning $_
}
}
}
$result | Export-Csv c:\temp\file.csv -NoTypeInformation
If you change your select statement to:
select-object #{n='groupName';expression={$group}}, samAccountName, distinguishedName
It should output the $group input of Get-ADGroupMember for that column.
I'm trying to combine the output of two functions with the output of the default Get-ADUser-cmdlet. I'm interested in when an account was created, if it's locked and what it's name is. I also want to know when the user logged on for the last time (using multiple DC's) and if the account is being used as a shared mailbox.
I've written two custom functions Get-ADUserLastLogon and isSharedMailbox, both functions use the Write-Output function to output their output. In case of Get-ADUserLastLogon this will be Lastlogon: time and in case of isSharedMailbox this will be shared: yes/no. I'm also using a standard Get-ADUser call in a foreach loop
Now, the default output of Get-ADUser is:
SAMAccountName LockedOut Created
-------------- --------- -------
ACC False 23-10-2015 8:20:20
Output of the custom functions is as following:
Lastlogon : 1-1-1601 1:00:00
Shared: yes
What I would like is to combine the LastLogon and Shared 'headers' to be combined into the Get-ADUser. So the output would become:
SAMAccountName LockedOut Created LastLogon Shared
Code of current code, where the accounts get imported from an Excel sheet:
foreach($username in $usernameWithTld){
if ($username -eq $NULL){
break
}
$usernameWithoutTld = $username.split('\')
Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName,
Created -ErrorAction Stop | Select-Object SAMAccountName, LockedOut,
Created
Get-ADUserLastLogon -UserName $usernameWithoutTld[1]
# Shared mailbox?
isSharedMailbox -mailboxname $usernameWithoutTld[1]
}
Function code:
function isSharedMailbox([string]$mailboxname){
$isObject = Get-ADUser -Filter {name -eq $mailboxname} -SearchBase "..." | Select-Object DistinguishedName,Name
if ($isObject -match "DistinguishedName"){
$output = "Shared: no"
Write-Output $output
} else {
$output = "Shared: No"
Write-Output $output
}
}
function Get-ADUserLastLogon([string]$userName){
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
Write-Output "LastLogon : $dt"
}
I'm sure there are lots of improvements that can be made, I'm still learning how to write (proper) PowerShell. I hope someone can answer my question.
You could use a Calculated Property in your Select-Object. Have a look at example 4 for the MSDN page.
In your case this would be:
Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName, Created -ErrorAction Stop | `
Select-Object SAMAccountName, LockedOut, Created, #{Name='LastLogon';Expression={Get-ADUserLastLogon -UserName $usernameWithoutTld[1]}}, #{Name='IsSharedMailbox';Expression={isSharedMailbox -mailboxname $usernameWithoutTld[1]}}
Or even better, you can use the object(s) that Get-ADUser puts in the pipeline to in turn call your functions for that specific object, and can be useful in case your query returns multiple results:
Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName, Created -ErrorAction Stop | `
Select-Object SAMAccountName, LockedOut, Created, #{Name='LastLogon';Expression={Get-ADUserLastLogon -UserName $_.sAMAccountName}}, #{Name='IsSharedMailbox';Expression={isSharedMailbox -mailboxname $_.sAMAccountName}}
One way to do this is to get your functions to return the values you are interested in, store them in variables, and combine everything together afterwards into a PSObject containing the properties you are interested.
The benefits of storing as an object are many. For example, you can use Select-Object, Sort-Object etc in the pipeline, or Export-CSV and other Cmdlets that expect InputObject
foreach($username in $usernameWithTld){
if ($username -eq $NULL){
break
}
$usernameWithoutTld = $username.split('\')
$adDetails = Get-ADUser $usernameWithoutTld[1] -Properties LockedOut, SamAccountName,
Created -ErrorAction Stop | Select-Object SAMAccountName, LockedOut,
Created
$lastlogin = Get-ADUserLastLogon -UserName $usernameWithoutTld[1]
# Shared mailbox?
$isshared = isSharedMailbox -mailboxname $usernameWithoutTld[1]
# putting together the PSobject
[array]$myResults += New-Object psobject -Property #{
SAMAccountName = $adDetails.SAMAccountName
LockedOut = $adDetails.LockedOut
Created = $adDetails.Created
LastLogon = $lastlogin
Shared = $shared # true/false or yes/no, depending on function
#Shared = if($shared){"yes"}else{"no"} # yes/no, based on true/false from function
}
}
Functions:
function isSharedMailbox([string]$mailboxname){
$isObject = Get-ADUser -Filter {name -eq $mailboxname} -SearchBase "..." | Select-Object DistinguishedName,Name
return ($isObject -match "DistinguishedName") # returns true/false
<# if you prefer to keep yes/no
if ($isObject -match "DistinguishedName"){
return "Yes" # no in original code
} else {
return "No"
}
#>
}
function Get-ADUserLastLogon([string]$userName){
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
return $dt
#Write-Output "LastLogon : $dt"
}
You can store the result of the functions in global variables and finally concatenate them is one way.
Else you can use return the output from the function and use the value later or like : $value= functionname then $value will hold the return value of the function and later you can combine the results.
function isSharedMailbox([string]$mailboxname){
$isObject = Get-ADUser -Filter {name -eq $mailboxname} -SearchBase "..." | Select-Object DistinguishedName,Name
if ($isObject -match "DistinguishedName"){
$output = "Shared: no"
$Global:result1= $output
} else {
$output = "Shared: No"
$Global:result1= $output
}
}
function Get-ADUserLastLogon([string]$userName){
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$time = 0
foreach($dc in $dcs)
{
$hostname = $dc.HostName
$user = Get-ADUser $userName | Get-ADObject -Properties lastLogon
if($user.LastLogon -gt $time)
{
$time = $user.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
$Global:result2= "LastLogon : $dt"
}
## Calling the function . Change the placeholders accordingly
Get-ADUserLastLogon -UserName $usernameWithoutTld[1]
isSharedMailbox -mailboxname $usernameWithoutTld[1]
$FinalResult = "result1" + "result2"
$FinalResult
Hope it helps you better understanding.
I have a list of users full name that I'd like to get their SAMaccount name but when I run my code I get no results. Anyone have any ideas?
$users = Get-Content C:\users\admin\Desktop\move.txt
foreach ($user in $users){
Get-ADUser -Filter {Name -eq "$user"} |Select-Object name, samaccountname
}
It could be that $user is null inside the script block. Try to use double quotes instead of braces and put the variable in single quotes to make the valid query (name contain spaces):
Get-ADUser -Filter "Name -eq '$user'" | Select-Object name, samaccountname
Here is a powershell script that should work for you. You'll want to go to the link to read the fine details, http://wbarena.com/2015/01/powershell-find-ad-user-full-name.html.
Import-Module ActiveDirectory
$aResults = #()
$List = Get-Content “.\List.txt”
ForEach($Item in $List){
$Item = $Item.Trim()
$User = Get-ADUser -Filter{displayName -like $Item -and SamAccountName -notlike “a-*” -and Enabled -eq $True} -Properties SamAccountName, GivenName, Surname, telephoneNumber, mail
$hItemDetails = New-Object -TypeName psobject -Property #{
FullName = $Item
UserName = $User.SamAccountName
Email = $User.mail
Tel = $User.telephoneNumber
}
#Add data to array
$aResults += $hItemDetails
}
$aResults | Export-CSV “.\Results.csv”
Use first and last name separately.
foreach ($user in $users){
$SplitName = -split $user
Get-ADUser -Filter {(GivenName -eq $SplitName[0]) -and (Surname -eq $splitName[1])} |Select-Object name, samaccountname
}
I have a requirement to generate a CSV report to get group members. However, I there are many child domains which contains groups starting with ADM.
I need report in the following format:
GroupName User Company LasLogon CN
ADM_AM UserOne CP1
I've found one script on internet:
Get-ADGroup -Server dc1.chd1.pd.local -Filter 'Name -like "ADM*"' |
ForEach-Object{
$hash=#{GroupName=$_.Name;Member=''}
$_ | Get-ADGroupMember -ea 0 -recurs |
ForEach-Object{
$hash.Member=$_.Name
New-Object psObject -Property $hash
}
} |
sort groupname,member
This script only gives me GroupName and UserName but not other information.
How can I generate this report?
I'm not sure what "ADM_AM, UserOne, CP1" is, but i got this much for you. I'm still new to powershell so forgive me if this is a lot of code =)
$array = #()
Foreach ($group in (Get-ADGroup -Server dc1.chd1.pd.local -Filter 'Name -like "ADM*"'))
{
$hash=#{Username ='';GroupName=$group.Name;Company='';LastLogon='';CN=''}
$members = $hash.GroupName | Get-ADGroupMember -Recursive -ErrorAction SilentlyContinue
Foreach($member in $members)
{
$properties = $member.SamAccountName | Get-ADUser -Properties SamAccountName, Company, lastLogon, CN
$hash.Username = $properties.SamAccountName
$hash.Company = $properties.Company
$hash.LastLogon = $properties.lastLogon
$hash.CN = $properties.CN
$obj = New-Object psObject -Property $hash
$array += $obj
}
}
$array | Export-Csv C:\ -NoTypeInformation
Here is what I would do, Im sure you can shorten it. You shoud specify a searchbase. Once you have the members samaccountname, you can use Get-ADUser to get whatever fields you want.
$GrpArr = #()
$Groups = get-adgroup -filter {name -like "adm*"} -searchbase "ou=Groups,dc=all,dc=ca" | select samaccountname
foreach ($group in $groups)
{
$GrpArr += $group
$members = get-adgroupmember $group | select samaccountName
foreach ($member in $members)
{
$memprops = get-aduser $member -properties company
$comp = $memprops.company
$grpArr += "$member,$comp"
}
}
$grpArr | export-csv c:\temp\Groups.csv -NoTypeInformation