Simple Adsearch for emails - powershell

I'm trying to get a list of emails from an ADGroup. The problem is there are members in the group that have no emails and I want the variable to show the ADuser instead of the email if the field is empty.
current code is this
$emails = get-adgroupmember gg-sccm-admins | get-aduser -Properties emailaddress | select emailaddress
$emails+= get-adgroupmember gg-sccm-site_admins | get-aduser -Properties emailaddress | select emailaddress
Write-Output $emails
My idea was to use an IF and if the email field is empty write username in the variable but I can't get it to work.

I guess you could use below:
$emails = get-adgroupmember gg-sccm-admins | Get-ADUser -Properties emailaddress | select #{N="EmailAddress";E={if($_.emailaddress){$_.emailaddress}else{$_.samaccountname}}}
$emails+= get-adgroupmember gg-sccm-site_admins | Get-ADUser -Properties emailaddress | select #{N="EmailAddress";E={if($_.emailaddress){$_.emailaddress}else{$_.samaccountname}}}
Write-Output $emails

Why not just do both?
[System.Collections.ArrayList]$Emails = #()
[System.Collections.ArrayList]$Names = #()
[array]$GroupMembers = Get-ADGroupMember "gg-sccm-admins" | Get-ADUser -Properties emailaddress,DisplayName | Select EmailAddress, DisplayName
Foreach($user in $GroupMembers){
$null = $Emails.Add($User.EmailAddress)
$null = $Names.Add($User.DisplayName)
}
For($i=0;$i -lt $Names.count;$i++){
[pscustomobject]#{
"User Names" = $Names[$i]
"Emails" = $Emails[$i]
}
}
Also, instead of using a fixed array, I changed it to an ArrayList. Should make the process faster as it doesn't have to recreate each array each time with a new item.

Related

Filtering multiple null values from Get-ADUser

I'm still working on learning powershell. I need to pull email and manager email from AD accounts in a group but exclude any records that have manager blank or email blank. I don't think I can 'where' for two where conditions, so I can get one or the other.
Get-ADGroupMember -Identity "groupname" -Recursive | Get-ADUser -properties * | where manager -NE $null | select displayname, EmailAddress, #{Name="ManagerEmail";Expression={(Get-ADUser -property Emailaddress $_.manager).emailaddress}} | export-csv -path c:\data.csv -NoTypeInformation
How would I go about getting both filtered out?
You can use and should use the Active Directory Filter or LDAP Filter for this instead of doing the filtering with powershell:
$group = (Get-ADGroup groupname).DistinguishedName
$managerMap = #{}
$params = #{
LDAPFilter = "(&(memberof:1.2.840.113556.1.4.1941:=$group)(mail=*)(manager=*))"
Properties = 'mail', 'manager'
}
Get-ADUser #params | ForEach-Object {
if(-not $managerMap.ContainsKey($_.manager)) {
$managerMap[$_.manager] = (Get-ADUser $_.manager -Properties mail).mail
}
[pscustomobject]#{
DisplayName = $_.DisplayName
EmailAddress = $_.mail
ManagerEmail = $managerMap[$_.manager]
}
} | Export-Csv 'c:\data.csv' -NoTypeInformation
Details of the LDAP Filter:
(& # AND, all conditions must be met
(memberof:1.2.840.113556.1.4.1941:=$group) # user is a member of `$group` (recursively)
(mail=*) # mail attribute is not null
(manager=*) # manager attribute is not null
) # closing AND

Is there a way in powershell to select what info needs to be the header so i can construct it to a table

I want to have a script that i can start and it gives me all the groups starting with APS- and its members
I want to get a HTML where it says in the Header : the group and in the table the Users
this is the code i got so far :
$properties = 'GivenName', 'Surname', 'UserPrincipalName'
Get-ADGroup -Filter {name -like "APS-*"} | ForEach {
$groupName = $_.Name
Get-ADGroupMember -Identity $_.SamAccountName |Get-ADUser -Property $properties |Select
#{N='GroupName';E={$groupName}},'GivenName', 'Surname', 'UserPrincipalName'}
But this give me a list of group,givenname,Surename,userprincipal
i want to get :
Organized like this
Is there a way of getting this ?
please consider the following points in order to accomplish the needed result
the variable $properties contains common values, you do not need to pass it to `Get-ADUser to get its values
You need to specify the parameter recursive to the command Get-ADGroupMember in order to get all child users within child groups.
If you need to output the result in HTML format you can use the command ConvertTo-Html
I modified your code to output the needed format, please check it
$groups = Get-ADGroup -Filter {name -like "APS-*"}
$list = foreach ($group in $groups) {
$groupName = $group.Name
Get-ADGroupMember -Identity $group.SamAccountName -Recursive | Get-ADUser | select #{N='GroupName';E={$groupName}},'GivenName', 'Surname', 'UserPrincipalName'
}
$out = $list | Group-Object GroupName
[string]$out_html = foreach ($item in $out){
$item.group | Select-Object 'GivenName', 'Surname', 'UserPrincipalName' | ConvertTo-Html -PreContent "<br>$($item.Name)<br><br>"
}
$out_html| Out-File .\groups.html
if you need to exclude specific users from the report, you can filter the output of Get-ADGroupMember using the Where clause as follow
# i.e. to exclude specific samaccountnames
$execlusionList = #("SamAccountName1","SamAccountName2")
Get-ADGroupMember -Identity $group.SamAccountName -Recursive | where {$_.samaccountname -notin $execlusionList} | Get-ADUser | select #{N='GroupName';E={$groupName}},'GivenName', 'Surname', 'UserPrincipalName'

Get-ADUser across domains - Help needed

I have the following code:
$FilePath_Prefix = "C:\temp\UserLastLogon-"
function Msg ($Txt="") {
Write-Host "$([DateTime]::Now) $Txt"
}
#Cycle each DC and gather user account lastlogon attributes
$List = #() #Define Array
(Get-ADDomain).ReplicaDirectoryServers | Sort | % {
$DC = $_
Msg "Reading $DC"
$List += Get-ADUser -Server $_ -Filter "samaccountname -like '*'" -Properties LastLogon |
Select samaccountname, lastlogon, #{n='DC';e={$DC}}
}
Msg "Sorting for most recent lastlogon"
$LatestLogOn = #() #Define Array
$List | Group-Object -Property samaccountname | % {
$LatestLogOn += ($_.Group | Sort -prop lastlogon -Descending)[0]
}
$List.Clear()
$FileName = "$FilePath_Prefix$([DateTime]::Now.ToString("yyyyMMdd-HHmmss")).csv"
try {
$LatestLogOn |
Select samaccountname, lastlogon,
#{n='lastlogondatetime';e={[datetime]::FromFileTime($_.lastlogon)}}, DC |
Export-CSV -Path $FileName -NoTypeInformation -Force
Msg "Exported results. $FileName"
} catch {
Msg "Export Failed. $FileName"
}
I use it to interrogate AD Users for most up-to-date lastLogon information across all my domains. It works, and it works really fast.
Now, I need to get more details into my output, such as givenName and Surname lets say.
What would be the best approach to achieve this, because I don't want to interrogate redundantly all my DC's for those kind of attributes.
My idea here, is to create another array with Get-ADuser -Filter * -Properties givenName, surname, etc..etc and then bind together the two arrays. And I don't seem to get it right. Could someone help, or point me in the right direction to achieve this task.
I would fetch all the last logon information and save it into a hash table, which is designed for fast lookups. I'd try something like this:
$FileName = 'C:\temp\UserLastLogon-{0:yyyyMMdd-HHmmss}.csv' -f [DateTime]::Now
$DCs = (Get-ADDomain).ReplicaDirectoryServers | Sort-Object
# This *may* be more semantically accurate; I don't remember how it works with multiple domains
# $DC = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName | Sort-Object
# Create the hash table
$UserLogonInfo = #{}
foreach ($DC in $DCs) {
Write-Host "Reading logon data from $DC..."
# Fetch all users that have a LastLogon value from the current DC
# We specify LastLogon>=1 because some users that never log on have LastLogon of
# 0 or null, both of which would show up as Jan 1, 1601.
Get-ADUser -Server $DC -LDAPFilter '(LastLogon>=1)' -Properties LastLogon | ForEach-Object {
if (!$UserLogonInfo.ContainsKey($_.DistinguishedName)) {
# If the accountname doesn't exist, add it
$UserLogonInfo[$_.DistinguishedName] = #{
LastLogon = $_.LastLogon
LastLogonDateTime = ([DateTime]::FromFileTime($_.LastLogon))
LastLogonDC = $DC
}
}
elseif (($UserLogonInfo[$_.DistinguishedName].LastLogon -lt $_.LastLogon)) {
# If the account name exists, update it if it's more recent
$UserLogonInfo[$_.DistinguishedName] = #{
LastLogon = $_.LastLogon
LastLogonDateTime = ([DateTime]::FromFileTime($_.LastLogon))
LastLogonDC = $DC
}
}
}
}
Write-Host "Fetching user data..."
Get-ADUser -Filter * -Properties LastLogon, givenName, surname |
Select-Object -Property SamAccountName, givenName, surname,
#{n='LastLogonDateTime';e={$UserLogonInfo[$_.DistinguishedName].LastLogonDateTime}},
#{n='LastLogonDC';e={$UserLogonInfo[$_.DistinguishedName].LastLogonDC}} |
Export-CSV -Path $FileName -NoTypeInformation -Force
If an account has a blank LastLogonDateTime and blank LastLogonDC, then that account has never logged on.
It's more correct to use DistinguishedName instead of SamAccountName as the key for the $UserLogonInfo hash table, and that is essentially a required change if you are querying multiple domains at once. Note that I do mean multiple domains and not merely multiple domain controllers in the same domain (which is what I believe you're actually doing in spite of the question title).
This whole process on my domain with 3 DCs and ~10,000 users takes about 15 seconds.
Note that there are a ton of ways that LastLogon can be inaccurate. It can be updated without a full logon or without an interactive logon and some logons won't force an update of the field. If you really want to track logons, you should use security auditing for logon events.
Edit:
When we populate $UserLogonInfo we're fetching all accounts except for accounts that either don't have a LogonDate attribute at all or when that attribute is 0 or null. Each of those indicate that there has been no login. So, we know that any user that isn't in the $UserLogonInfo hash table has never logged in.
If you want to use some special value for when a user account has never logged on, you should just use an if statement and check to see if the user is in the $UserLogonInfo hash table:
Get-ADUser -Filter * -Properties LastLogon, givenName, surname |
Select-Object -Property SamAccountName, givenName, surname,
#{n = 'LastLogonDateTime'; e = {if ($UserLogonInfo.ContainsKey($_.DistinguishedName)) { $UserLogonInfo[$_.DistinguishedName].LastLogonDateTime } else { 'Never' }}},
#{n = 'LastLogonDC'; e = {if ($UserLogonInfo.ContainsKey($_.DistinguishedName)) { $UserLogonInfo[$_.DistinguishedName].LastLogonDC } else { 'N/A' }}} |
Export-CSV -Path $FileName -NoTypeInformation -Force
The thing is that you're actually asking your DCs for that data as it's returned by default. So the only thing to add is
, givenName, surname
to your Select-Object. Check this by running
Get-AdUser yourLogin -Properties LastLogon
You'll receive the following properties in your output: DistinguishedName Enabled GivenName LastLogon
Name
ObjectClass
ObjectGUID
SamAccountName
SID
Surname
UserPrincipalName
So the only thing you lose will be some memory. The alternative approach would be to create another array using
$names = Get-ADuser -Filter * | Select-Object SamAccountName, givenName, surname
and then match the date based on SamAccountName like this:
$LatestLogOn = #() #Define Array
$List | Group-Object -Property samaccountname | % {
$t = ($_.Group | Sort -prop lastlogon -Descending)[0]
$n = $names | Where-Object samaccountname -eq $t.samaccountname
$LatestLogOn += $t | select *, #{n="givenName";e={$n.givenName}}, #{n="surname";e={$n.surname}}
}

Get-ADGroup - Group Name, ManagedBy Name and Email

I'm looking to get the the Group Name, Managed By Name and Managed by Email in a PowerShell query similar to this.
Get-ADGroup -filter {Name -like "*Admins" }
The output would look something similar to:
Group Name | Managed By Name | Managed By Email
The issue I'm having is with joining Get-ADGroup and Get-ADUser. In SQL this "join" would happen on get-adgroup.managedby = get-aduser.distinguishedname. I know that's not how it works in Powershell, just thought I'd throw out an example of what I'm trying to do.
Any help would both be welcomed and appreciated.
I see #Mathias R. Jessen beat me to it, but here's what I had:
Get-ADGroup -filter {Name -like "*IT*" } -Properties managedBy |
ForEach-Object {
$managedBy = $_.managedBy;
if ($managedBy -ne $null)
{
$manager = (get-aduser -Identity $managedBy -Properties emailAddress);
$managerName = $manager.Name;
$managerEmail = $manager.emailAddress;
}
else
{
$managerName = 'N/A';
$managerEmail = 'N/A';
}
Write-Output $_; } |
Select-Object #{n='Group Name';e={$_.Name}}, #{n='Managed By Name';e={$managerName}}, #{n='Managed By Email';e={$managerEmail}}
You can do it like this:
$Groups = Get-ADGroup -Filter { Name -like "*Admins" } -Properties managedBy,mail
$Groups |Select-Object Name,#{Name='ManagedBy';Expression={(Get-ADUser $_.managedBy).Name}},Mail
The #{} syntax after Select-Object is known as a calculated property.
You could also pipe the groups to ForEach-Object and call Get-ADUser inside the process scriptblock:
Get-ADGroup -Filter {Name -like "*Admins"} -Properties managedBy,mail |ForEach-Object {
# Find the managedBy user
$GroupManager = Get-ADUser -Identity $_.managedBy
# Create a new custom object based on the group properties + managedby user
New-Object psobject -Property #{
Name = $_.Name
ManagedBy = $GroupManager.Name
Email = $_.mail
}
}
I would something do like this:
# Get all groups into a variable
$Group = Get-ADGroup -Filter {Name -like "*Admin*"} | Select-Object -expandProperty Name
foreach ($Groups in $Group){
# Get ManagedBy name for each group (If ManagedBy is empty group wil not be listed)
$User = Get-ADGroup $Groups -Properties * | Select-Object -ExpandProperty ManagedBy
foreach ($Users in $User){
# Get Name and EmailAddress for each User
$Name = Get-ADUser $Users | Select-Object -ExpandProperty Name
$Email = Get-ADUser $Users -Properties * | Select-Object -ExpandProperty EmailAddress
# Write output
Write-Host $Groups "," $Name "," $Email
}
}
Hope this helps.

Export AD users with list of specific groups

I've been trying to get an extract of AD users and select mail, name, memberof. I then need to list only specific groups from the memberof output so I end up with a list for each user than contains their name, email address and specific groups that match a certain name and not all of the groups they are a member of.
Get-ADUser username -Properties memberof | Select-Object memberof
I can't seem to find a way of doing this as I end up with either noteproperty above or an empty pipeline. Is there a way to achieve what I am trying to do?
The memberOf attribute contains a list of distinguishedName (DN) values, each corresponding to a group.
Retrieve the groups you are interested in, before you run Get-ADUser, that way you can compare the Group DN to the entry in memberOf:
$GroupDNs = Get-ADGroup -Filter {Name -like "*finance*" -or Name -like "*creditcontrol*"} | Select-Object -ExpandProperty DistinguishedName
Now, you can use those DN's to filter the group memberships with a calculated property, like so:
$UserInfo = foreach($username in #("bob","alice","joe")){
$User = Get-ADUser -Identity $username -Properties mail,memberOf |Select Name,mail,memberof
$User | Select-Object Name,mail,#{Label="GroupDNs";Expr = {$_.memberof | Where-Object {$Groups -contains $_}}}
}
without doing a new Get-ADGroup query for each memberof entry.
If you want a string of group names, rather than a NoteProperty containing an array of strings, you could fill the Groups into a hashtable and use that to "look up" the memberof entries using the ContainsKey() method:
$Groups = #{}
Get-ADGroup -Filter {Name -like "*finance*" -or Name -like "*creditcontrol*"} | ForEach-Object {
$Groups[$_.DistinguishedName] = $_
}
$UserInfo = foreach($username in #("bob","alice","joe")){
$User = Get-ADUser -Identity $username -Properties mail,memberOf |Select Name,mail,memberof
$User | Select-Object Name,mail,#{Label="Groups";Expr = { ($_.memberof | Where-Object {$Groups.ContainsKey($_)} | ForEach-Object { $Groups[$_].Name}) -join ";" }}
}
$UserInfo | Export-Csv C:\aduserinfo.csv -NoTypeInformation