bit of a long one...
I have code that splits up an input, searches for a username. If it can find one - it should send an email to the technician (who is in the input as well) saying the username is ready. If it finds multiple, I need it to send a list of all the possible ones to the technician. If it can't find any, I want it to ignore that user completely. I am struggling being able to send an email from Powershell itself. Has anyone got any ideas about the below?
$list holds the information
$names = $list | ForEach-Object { $_.Split(",")[0] };
$name = $names | ForEach-Object { $_.Split("-")[1]} #Gets the actual names for the users
$emails = $list | ForEach-Object { $_.Split(",")[2]} #Gets the technician email addresses
$count = 0
foreach ($test in $name) {
$indexemails = $emails[$count]
$a,$b,$c = $test | ForEach-Object { $_.Split(" ")}
$displaynamedetails = Get-ADUser -filter {GivenName -eq $b -and Surname -eq $c} | Select-Object samAccountName
if ($displaynamedetails -eq $null) {
$count ++}
else {
$final += $displaynamedetails, $indexemails
$count ++
}}
Thank you in advance
Related
I got a small issue. I have a CSV with some names. I needed to get the email addresses of those users, so I created a small script that will find users with that firstname and lastname in AD.
After that, I wanted to export it to a new CSV file, but keep the phone-numbers from the first CSV file so its also available in the new CSV export.
That last part don't seem to work. In the output that expression is shown as {}.
Can someone help me?
$csv = import-csv -path C:\users\da6\desktop\UsersZonderEmail.csv
$output = #()
foreach ($i in $csv) {
$givenname = $i.Voornaam
$lastname = $i.Achternaam
$number = $i.IpPhone
$output += try {Get-ADUser -Filter {givenname -eq $givenname -and surname -eq $lastname} | select Name, UserPrincipalName, #{ name="ipphone"; expression=$number}} catch {}
}
Basically what is wrong with your code is that you forgot the opening bracket { in the expression oart of the calculated property.
Also note that -Filter should be a string, not a scriptblock.
Lastly, adding to an array with += is a bad habit, as the entire array needs to be rebuilt in memory on every addition.
Better let PowerShell collect the values from the loop:
$csv = Import-Csv -Path 'C:\users\da6\desktop\UsersZonderEmail.csv'
$output = foreach ($item in $csv) {
Get-ADUser -Filter "GivenName -eq '$($item.Voornaam)' -and Surname -eq '$($item.Achternaam)'" -Properties EmailAddress |
Select-Object Name, UserPrincipalName, #{ Name="ipphone"; Expression = {$item.IpPhone}}, EmailAddress
}
# output to new CSV file
$output | Export-Csv -Path 'C:\users\da6\desktop\UsersMetEmail.csv' -NoTypeInformation
I'm struggling with reading and writing a csv file using power shell. I need to read a file containing three columns Servers, Users, Status
I've completed the first step (read in the file)
The next step is to split the data in the Status field by its delimiter ';' (I have that as well)
Now where the problem comes in is where I loop thru a Get-ADUser and build the print line putting all the values in the foreach loop together - seperated with a ';'. (new status field)
Then of course at the conclusion write out the completed file.
so the input file is a csv that contains the following
Server Users Status
ABC xtom
JKL
CDEA msall;jbec
The output csv would be
Server Users Status
ABC xtom xert.tom#abc.com;
JKL
CDEA msall;jbec mik.sall#abc.com;jay.bec#abc.com
my code as it stands
$List = Import-Csv -Path "c:\data\infile.csv"
$outarray = #()
foreach ($item in $List)
{
Write-output $item
$Servers= $($item.Server)
$Users = $($item.Users)
$Status = $($item.Status)
if ($Users.length -gt 0)
{
$CharArray =$Users.Split(";")
Foreach ($i in $CharArray)
{
$email = Get-ADUser -Filter {SamAccountName -like $i} | Select -Property UserPrincipalName
$outarray = $email
}
}
}
$outarray | Export-Csv "c:\data\testout.csv" -noType
Any help would be appreciated
Thanks
The csv input file
Server,Users,Status
ABC,xtom,
JKL,,
CDEA,msall;jbec,
the csv output file
Server,Users,Status
ABC,xtom,xert.com#abc.com
JKL,,
CDEA,msall;jbec,mik.sall#abc.com;jay.bec#abc.com
So, if I understand correctly what you need this code should work for you:
$List = Import-Csv -Path "c:\data\infile.csv"
$outarray = [system.collections.generic.list[pscustomobject]]::new()
foreach ($item in $List)
{
Write-output $item
$Server = $($item.Server)
$Users = $($item.Users)
$Status = $($item.Status)
if ($Users)
{
$CharArray = $Users.Split(";")
$email = $(
Foreach ($i in $CharArray)
{
$usr=(Get-ADUser -Filter {SamAccountName -like $i}).UserPrincipalName
if($usr){$usr}
}) -join ';'
}
$outarray.Add(
[PSCustomObject]#{
Server = $Server
Users = $Users
Status = $email
})
}
$outarray | Export-Csv "c:\data\testout.csv" -NoTypeInformation
It's worth mentioning that userPrincipalName is definitely not the same thing as mail even though they can look the same this may not always be the case.
Also, if $i will actually contain samAccountNames then -Filter {....} is not needed.
I know this is pulling quite a bit of data, but at present it's capping my memory consumption when I run it on my local machine. The good news is, it's returning the output that I need. Can someone help me with performance optimization? So far, I haven't done much for fear of messing up a script that returns my desired output. Thanks in advance for any suggestions.
#// Start of script
#// Get year and month for csv export file
$DateTime = Get-Date -f "yyyy-MM"
#// Set CSV file name
$CSVFile = "C:\Temp\AD_Groups"+$DateTime+".csv"
#// Create emy array for CSV data
$CSVOutput = #()
Measure-Command {
#// Get all AD groups in the domain
$ADGroups = Get-ADGroup -Filter "GroupScope -ne 'DomainLocal' -AND GroupCategory -eq 'Security' -AND Member -like '*'" -SearchBase "OU=SHS, DC=shs, DC=net" -Properties Member #-ResultSetSize 1000 Name -like '*''s*' -AND
#// Set progress bar variables
$i=0
$tot = $ADGroups.count
foreach ($ADGroup in $ADGroups) {
#// Set up progress bar
$i++
$status = "{0:N0}" -f ($i / $tot * 100)
Write-Progress -Activity "Exporting AD Groups" -status "Processing Group $i of $tot : $status% Completed" -PercentComplete ($i / $tot * 100)
#// Ensure Members variable is empty
$Members = ""
#// Get group members which are also groups and add to string
$MembersArr = Get-ADGroup $ADGroup.DistinguishedName -Properties Member | Select-Object -ExpandProperty Member
if ($MembersArr) {
foreach ($Member in $MembersArr) {
$ADObj = Get-ADObject -filter {DistinguishedName -eq $Member}
#// Initialize regex variable
$matches = ""
if ($ADObj.ObjectClass -eq "user") {
$UserObj = Get-ADObject -filter {DistinguishedName -eq $Member}
$match = $UserObj -match '\([a-zA-Z0-9]+\)'
$empid=$matches[0] -replace ".*\(","" -replace "\)",""
if ($UserObj.Enabled -eq $False) {
continue
}
$Members = $empid
}
# Check for null members to avoid error for empty groups
if ([string]::IsNullOrEmpty($Members)) {
continue
}
$HashTab = [ordered]#{
"GroupName" = $ADGroup.Name -replace "'s", "''s"
"GroupCategory" = $ADGroup.GroupCategory
"GroupScope" = $ADGroup.GroupScope
"MemberID" = if([string]::IsNullOrEmpty($empid)){""}
else{$empid}
}
#// Add hash table to CSV data array
$CSVOutput += New-Object PSObject -Property $HashTab
}
}
#// Export to CSV files
$CSVOutput | Sort-Object Name, Member | Export-Csv $CSVFile -NoTypeInformation
}
}
I've experienced this too with code that loops through thousands of accounts. The problem is that the garbage collector doesn't have time during the loop to clean up, since your code is constantly doing something. In .NET, I'd call .Dispose() manually to make sure stuff is cleaned up, but here you can't.
You can try calling [System.GC]::Collect() after you assign each variable in the loop. For example, after $MembersArr = and after $ADObj = to (hopefully) make it deallocate the memory used for the previous value.
Also, I think that $UserObj = Get-ADObject... line should be calling Get-ADUser, not Get-ADObject. As it is, $UserObj.Enabled will never have a value and your continue will never be hit.
But you can save yourself the use of Get-ADUser entirely by asking for the userAccountControl value in Get-ADObject and using that to determine if the user is disabled. For example:
$ADObj = Get-ADObject -filter {DistinguishedName -eq $Member} -Properties userAccountControl
# Clean up the old $ADObj value
[System.GC]::Collect()
#// Initialize regex variable
$matches = ""
if ($ADObj.ObjectClass -eq "user") {
$match = $ADObj -match '\([a-zA-Z0-9]+\)'
$empid=$matches[0] -replace ".*\(","" -replace "\)",""
if ($ADObj.userAccountControl -band 2) {
continue
}
$Members = $empid
}
The $ADObj.userAccountControl -band 2 condition checks is a bitwise AND comparison to check if the second bit of the userAccountControl value is set, which means that the account is disabled.
I'm trying to create a script that will pull a list of group members from AD and run a foreach loop to determine when the last time each user logged into any given domain controller. I got some of the code for the measure-latest function here . I would like to have the script run through the foreach loop and print the samAccountName (username) and last login time stamp (measure-latest) for each user in the group, but so far have not been able to get it working. I think I've got something wrong in logic but i can't seem to figure it out. Any help is appreciated, thank you.
# Get a list of last login times for a group of users
# Script Requires Quest Cmdlet features: https://support.software.dell.com/activeroles-server/download-new-releases
Add-PSSnapin Quest.ActiveRoles.ADManagement
# filter out $nulls and produce the latest of them
function Measure-Latest {
BEGIN { $latest = $null }
PROCESS {
if (($_ -ne $null) -and (($latest -eq $null) -or ($_ -gt $latest))) {
$latest = $_
}
}
END { $latest }
}
# Get list of group users by username
Get-ADGroupMember -identity "Domain Admins" | select samAccountName | Export-csv -path C:\Scripts\UserInformationByGroup\Groupmembers.csv -NoTypeInformation
# Get list of users from group, assign user value
$userlist = import-csv C:\Scripts\UserInformationByGroup\Groupmembers.csv
$user = $userlist | Select samAccountName
# Loop through list of users and print Username ------ Last Login time
foreach ($user in $userlist) {
Get-QADComputer -ComputerRole DomainController | foreach {
(Get-QADUser -Service $_.Name -SamAccountName $user).LastLogon
} | Measure-Latest $samAccountName | out-file -filepath C:\Scripts\UserInformationByGroup\userListLastLogin.txt -append
}
I should mention that when I run the script like this, and just enter each username manually it works and prints the last login time:
Add-PSSnapin Quest.ActiveRoles.ADManagement
function Measure-Latest {
BEGIN { $latest = $null }
PROCESS {
if (($_ -ne $null) -and (($latest -eq $null) -or ($_ -gt $latest))) {
$latest = $_
}
}
END { $latest }
}
Get-QADComputer -ComputerRole DomainController | foreach {
(Get-QADUser -Service $_.Name -SamAccountName USER_NAME_HERE).LastLogon
} | Measure-Latest
This part of the pipeline makes no sense:
| Measure-Latest $samAccountName |
Since nothing is assigned to the variable $samAccountName
You'll need to add pipeline support to your Measure-Latest function, like so:
function Measure-Latest {
[CmdletBinding]
param(
[parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
$LastLogon
)
# rest of the script (begin-process-end blocks) goes here
}
And now you can pipe the LastLogon values directly to the function:
(Get-QADUser -SamAccountName $user).LastLogon |Measure-Latest | Out-File #or whatever
Thanks to the ValueFromPipelineByPropertyName flag, you can now also pipe the entire object to the function:
Get-QADUser -SamAccountName $user | Measure-Latest
Because the parameter name ($LastLogon) matches that property anyways
In addition to that, I would probably change the logic a bit, so that you don't perform 1 LDAP query per DC per user. If you have 5 Domain Controllers and 200 users, that's now 1000 individual queries to the directory service.
You could simply retrieve ALL the users from each DC, in a single query, using the -LdapFilter parameter with Get-QADUser:
$LDAPClauses = #()
foreach($user in $userlist){
$LDAPClauses += "(samaccountname={0})" -f $user
}
# The | in an LDAP filter means logical OR
$LDAPFilter = "(&(LastLogon=*)(|$(-join($LDAPClauses))))"
Now you can run just a single query per DC:
Get-QADUser -LdapFilter $LDAPFilter
and it'll retrieve all the users in $userlist that has a LastLogon attribute value on that specific DC (you're not really interested in $null-values anyways, right?)
I need some help writing a script as i am struggling to understand the logic.
I basically have a list of user ids that i need to check to see if they have two certain AD groups. If they have, these need to be outputted into a csv and highlighted.
Can anyone help to get me started? I need to use the Quest Powershell cmdlets
Here is the code
$textFileContents = Get-Content C:\temp\powershell\users.txt
$results = #()
foreach($username in $textFileContents){
$groups = get-qaduser $username |select -expand memberof
if ($groups -match "grpuip1" -and $groups -match "group2"){
echo $group
}
}
check this to begin :
"user1","user2" | foreach {
$groups = get-qaduser $_ |select -expand memberof
if ($groups -match "GROUP1" -and $groups -match "GROUP2"){
echo $_
}
}
I'd use the cmdlet Get-QADMemberOf instead of Get-QADUser. There's nothing wrong with what you're doing, but it's retrieving more information than you need.
Try this to start with:
$textFileContents = Get-Content C:\temp\powershell\users.txt
# Rather than initializing the array, and adding new elements,
# just output each element of the loop to the pipeline, and
# assign the results of the whole pipeline to the variable.
# This is *much* faster than adding to an array
$results = $textFileContents | ForEach-Object {
$userGroups = Get-QADMemberOf $_
if ($userGroups -contains "group1" -and $userGroups -contains "group2") {
New-Object -TypeName PSObject -Property #{"UserName" = $_; "Groups" = ($userGroups -join ",");}
}
}
$results | ConvertTo-Csv -NoTypeInformation | Set-Content C:\Filename.txt