PowerShell - foreach loop every 1 minute - powershell

I have a script that queries AD and if it finds users created within 24 hours it will email them. I have this part working fine, but I can't seem to figure out why 1) it works line by line in PowerShell windows but not running the exact same code as a .ps1 file; and 2) why the $name field in the $mailBody is not displaying at all. It is displaying $username but not $name. I'll post the error message below the code.
clear
Import-Module ActiveDirectory
$secpasswd = ConvertTo-SecureString "xxxx" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("xxx#xxx.com", $secpasswd)
$PSEmailServer = "xxx#xxx.com"
$SMTPPort = 587
$SMTPUsername = "xxx#xxx.com"
$MailFrom = "xxx#xxx.com"
$MailSubject = "Your user account has been created"
#Pull users created within last 24 hours
$when = ((get-date).AddDays(-2)).addMinutes(-0) #past 24 hours from current time
$when
$users = Get-ADUser -Filter {whenCreated -ge $when} -Properties whenCreated,mail,givenname,surname,SAMAccountName,info | Select-Object whenCreated,mail,givenname,surname,SAMAccountName,info | where info -ne true
Foreach ($user in $users)
{
$users
$name = $user.name
$emailTo = $user.mail
$username = $user.SAMAccountName
$MailBody =" <p>Dear $name,
Your CDAT Account has been created. <br><br>
Your Username is $username <br><br>
Please contact CDAT support for further assistance. <br><br>
CDAT Support Team <br><br>
This email was auto-generated. Please do not reply. If you need assistance, please contact CDAT Support. <br><br>
Confidentiality Notice: This email message, including any attachments, is for the sole use of the intended recipient(s), and may contain confidential and privileged information. Any unauthorized review, use, disclosure, or distribution is prohibited. If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message.<br><br>
</P>"
Send-MailMessage -From $MailFrom -To $emailTo -Subject $MailSubject -Body $MailBody -BodyAsHtml -Port $SMTPPort -UseSsl -Credential $mycreds
Set-AdUser -identity $username –replace #{info=”true”}
}
"PS C:\scripts> .\EmailUsers24HoursAfterAccountCreation.ps1
At C:\scripts\EmailUsers24HoursAfterAccountCreation.ps1:33 char:36
+ Set-AdUser -identity $username –replace #{info=â€trueâ€}
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
The string is missing the terminator: ".
At C:\scripts\EmailUsers24HoursAfterAccountCreation.ps1:18 char:2
+ {
+ ~
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString"

Related

AD password update via Powershell

Too all Powershell Experts: I need some help to figure out why this is working incorrectly. What I mean incorrectly, the accounts in CSV password get updated with a random password and email is sent to the users but i don't under why they are getting the same password, and everyaccount in the AD is addressed. See email output:
Firstuser email:
Hello Guest DefaultAccount user1, user2, user3, user4 test John Doe,
Your AD Account user1, password has been updated to e,wQlsGfBE;J'4
Second useremail
Hello Guest DefaultAccount user1, user2, user3, user4 test John Doe,
Your AD Account jdoe, password has been updated to e,wQlsGfBE;J'4
what I'm trying to accomplish are as follows:
List item
Import one column cvs with just "samAccountName"
reset password of the accounts listed in the csv with a random generated 20 character long password
Email the AD account associated email address with the new password. (I'm doing this because their email address if different from their AD Account), so they are able to receive their email.
Import-Module ActiveDirectory
# Import AD user objects and store in users variables
$users = Get-ADUser -Filter * -Properties Name, EmailAddress,SamAccountName | Select SamAccountName,Name,EmailAddress
$Name = $users.Name
$SamAccountName = $users.SamAccountName
$EmailAddress = $users.EmailAddress
#Date
$date = (Get-Date -Format F)
#Generate a random password and store in newpwd
$newpwd = -join (33..126|%{[char]$_}|Get-Random -Count 20)
$newPassword = ConvertTo-SecureString -AsPlainText "$newpwd" -Force
# Import users from CSV
Import-Csv "C:\Userlist.csv" | ForEach-Object {
$samAccountName= $_."samAccountName"
#Proceed with password reset
Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
Set-AdUser -Identity $samAccountName -ChangePasswordAtLogon $true
#Server configuration
$smtpServer =""
$SmtpPort = ""
# sending option
$from = "it-operation#example.com"
$to = $EmailAddress
$subject ="AD Password updated - " + $date
$priority = "Normal"
$from = ""
$body = #"
Hello $name, <br>
Your AD Account $samAccountName, password has been updated to $newpwd
"#
try {
# Send the report email
Send-MailMessage -To $to -Subject $subject -BodyAsHtml -body $body -SmtpServer $smtpServer -Port $SmtpPort -From $From -Priority $priority
}
catch{
write-warning "error in sending. $_"
} # End User Processing
}
The Account password is getting updated, but just can't send the emai
When you do $Name = $users.Name, the $Name variable gets an array containing ALL usernames in the CSV.
Remove that line and instead set the variable inside your ForEach-Object loop:
# Import users from CSV
$UserList = Import-Csv "C:\Userlist.csv" | ForEach-Object {
$samAccountName= $_.samAccountName
$name = $_.Name
# then the rest of your code
}
Note:
SmtpPort takes an integer, not a string
From cannot be an empty string
P.S. Try to do some proper code indentation, so it is much easier to see where a code block (like ForEach-Object) starts and ends)
Your code revised:
Import-Module ActiveDirectory
# Import AD user objects and store in users variables
$users = Get-ADUser -Filter * -Properties Name, EmailAddress,SamAccountName | Select SamAccountName,Name,EmailAddress
#Date
$date = (Get-Date -Format F)
# Import users from CSV
Import-Csv "C:\Userlist.csv" | ForEach-Object {
# read this from the CSV
$samAccountName= $_.samAccountName
# get the user object from the $users array
$user = $users | Where-Object { $_.SamAccountName -eq $samAccountName }
$name = $user.Name
$emailAddress = $user.EmailAddress
#Generate a random password and store in newpwd
$newpwd = -join (33..126|%{[char]$_}|Get-Random -Count 14)
$newPassword = ConvertTo-SecureString -AsPlainText "$newpwd" -Force
$body = #"
Hello $name, <br>
Your AD Account $samAccountName, password has been updated to $newpwd
"#
#Proceed with password reset
Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
Set-AdUser -Identity $samAccountName -ChangePasswordAtLogon $true
#Server configuration
$mailParams = #{
SmtpServer = "YourSMTPServer"
Port = 25 # <-- an integer value
# sending option
From = "it-operation#example.com"
To = $user.EmailAddress
Subject ="AD Password updated - " + $date
Priority = "Normal"
Body = $body
BodyAsHtml = $true
}
try {
# Send the report email
Send-MailMessage #mailParams -ErrorAction Stop
}
catch{
write-warning "error in sending. $_"
} # End User Processing
}
Don't accept my answer. I'm just building off what #Theo did. (I'd love an up-vote though)
Import-Module ActiveDirectory
# Import AD user objects and store in users variables
$Users = #{}
Get-ADUser -Filter * -Properties Name,EmailAddress,SamAccountName | Select-Object SamAccountName,Name,EmailAddress |
ForEach-Object{ $Users.Add( $_.samAccountName, $_ ) }
#Date
$Date = (Get-Date -Format F)
# Generate a random password and store in newPassword
$newPassword = -join ( 33..126 | ForEach-Object{ [char]$_ }| Get-Random -Count 20 )
$newPassword = ConvertTo-SecureString -AsPlainText "$newPassword" -Force
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Move to the loop if you want each user's pwd to be different!!!
# Put this here so you can indent the loop easier...
$BaseBody =
#"
Hello %NAME%, <br>
Your AD Account %SAMACCOUNTNAME%, password has been updated to %NEWPASSWORD%
"#
# Server configuration & recipien configurations...:
$MailParams = #{
$smtpServer = "YorServer.example.com" #MAKE SURE YOU CHANGE THIS
$SmtpPort = 25
$from = "it-operation#example.com"
$To = ""
$subject ="AD Password updated - " + $date
$Body = ""
}
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# Removed -Priority if it's alwasy going to be normal you don't need it.
# Import users from CSV
Import-Csv "C:\Userlist.csv" |
ForEach-Object {
$samAccountName = $_."samAccountName"
# Proceed with password reset
Set-ADAccountPassword -Identity $samAccountName -NewPassword $newPassword -Reset
Set-ADUser -Identity $samAccountName -ChangePasswordAtLogon $true
# Get the Name & Email address from the $Users Hash...:
$EmailAddress = $Users[$samAccountName].EmailAddress
$Name = $Users[$samAccountName].Name
# Reset the To & Body values
$MailParams['To'] = $EmailAddress
$MailParams['Body'] = $BaseBody.Replace('%NAME%', $Name).Replace('%SAMACCOUNTNAME%', $samAccountName).Replace('%NEWPASSWORD%', $newPassword)
try { # Send the report email
Send-MailMessage #MailParams -ErrorAction Stop
}
catch{
write-warning "Error in sending : $_"
}
} # End User Processing
Used a hash table to store the AD user objects so you don't have to re-execute a Where{} on every loop iteration. Depending on the number of users this could be quite a bit faster. When relating data between 2 sources I gravitate toward a hash tables.
Moved the $Body var out of the loop. Here strings don't like to be indented, by moving it out we don't have to interfere with the loop indentation; improving readability. Then inside the loop we can get the actual body by doing some string replacement.
I also placed the $MailParams parameter hash outside the loop. Then inside the loop I reset the 2 keys that vary. Nitpicking, but I'd rather not declare the hash on every loop iteration either.
$Reused the $newPassword variable rather than have the extra $newpdw variable just laying around.
Expanded the aliases, just because PSScriptAnalyzer complains about it...
Obviously this is untested! But, I hope it's helpful...

How to send an email with the status of the scheduled tasks in Windows scheduler in Powershell

I would like to send an email with the updated status of the scheduled tasks set in Windows scheduler in Power Shell.
I am able to get the updated status of the scheduled tasks in Powershell via this command:
Get-ScheduledTask -TaskPath "\" | Get-ScheduledTaskInfo | Export-Csv -NoTypeInformation -Path C:\Lakshmen\schedu
ledTasksResults.csv
This exports the data into a csv. Instead I would like this information in an email. But I am not sure how to send this via a email.
Tried this:
Get-ScheduledTask -TaskPath "\" | Get-ScheduledTaskInfo | Send-MailMessage -To "lakesh#outlook.com" -From "lakesh#outlook.com" -Subject "ScheduledTasks" -SmtpServer "smtp.mail.outlook.com"
Got an error like this:
Send-MailMessage : Illegal characters in path.
At line:1 char:59
+ ... dTaskInfo | Send-MailMessage -To "lakesh#outlook.com" -From ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Send-MailMessage], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.SendMailMessage
Need some guidance on this.
One way would be to attach your result from:
Get-ScheduledTask -TaskPath "\" | Get-ScheduledTaskInfo | Export-Csv -NoTypeInformation -Path C:\Lakshmen\scheduledTasksResults.csv
Edit due to correct comment by #LotPing that spaltting (less is more) should be used instead of the variables (thanks).
Then sending the mail:
$Arguments = #{
From = "lakesh#outlook.com"
To = "lakesh#outlook.com"
Attachment = "C:\Lakshmen\scheduledTasksResults.csv"
Subject = "Here are my scheduled tasks"
Body = "See the attachments for the tasks results"
SMTPServer = "smtp.mail.outlook.com"
SMTPPort = "587"
}
Send-MailMessage #Arguments -UseSsl -Credential (Get-Credential) –DeliveryNotificationOption OnSuccess
Or you could simply put it into the body:
$Arguments = #{
From = "lakesh#outlook.com"
To = "lakesh#outlook.com"
Subject = "Here are my scheduled tasks"
Body = Get-ScheduledTask -TaskPath "\" | Get-ScheduledTaskInfo | Out-String
SMTPServer = "smtp.mail.outlook.com"
SMTPPort = "587"
}
Send-MailMessage #Arguments -UseSsl -Credential (Get-Credential) –DeliveryNotificationOption OnSuccess
Note: due to the Get-Credential it will prompt you for password. If you want it without user interaction you need to store your password it as SecureString.
First create a credential object. Then make sure to convert the object to string in order to be able to be sent. Personally I would send the CSV as attachment, but here is an example:
$emailAddress = "lakesh#outlook.com"
$secpasswd = ConvertTo-SecureString "yourPassword" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("$emailAddress", $secpasswd)
$tasks = Get-ScheduledTask -TaskPath "\" | Get-ScheduledTaskInfo | Out-String
Send-MailMessage -To $emailAddress -From $emailAddress -Subject "ScheduledTasks" -SmtpServer "smtp.mail.outlook.com" -Credential $mycreds -Body $tasks

Can't send powershell email to managers

I'm trying to create a PS script to email managers about employees with expiring accounts(NOT PASSWORDS) within the next 10 days.
I know this has been discussed already a few times but I was unable to find a suitable solution to this issue.
This is what I have so far:
$MyEmail = "email#domain.com"
$SMTP= "domain.com"
$To = "email#domain.com"
$Subject = "Account expiring"
$Body = "Hi_,
the following account is due to expire, please reply to this email if
you wish to extend the account for a further 3 months
Kind regards,
Tech Team"
$Users = Get-ADUser -filter * -SearchBase "OU=Powershell Test
OU,DC=domain,DC=com" -Properties emailaddress,
Manager,accountexpirationdate
$Users | Select
Name,emailaddress,accountexpirationdate,#{label="Manager";expression= .
{(Get-ADUser $_.Manager -Properties emailaddress).emailaddress}}
Send-MailMessage -To $to -From $MyEmail -Subject $Subject -Body $Body
-SmtpServer $SMTP
I believe I am missing a few pieces.
Can anyone help?
It would be much appreciated.
Thanks
So I've edited my previous comment, because I couldn't stand that is was still not very good:
$MyEmail = "email#domain.com"
$secpasswd = ConvertTo-SecureString "PlainTextPassword" -AsPlainText -Force
$SMTP= "domain.com"
$SMTPPort = "587"
$cred = New-Object System.Management.Automation.PSCredential ($MyEmail, $secpasswd)
$NeverExpires = 9223372036854775807;
$ExpireMin = (Get-Date).AddDays(0);
$ExpireMax = (Get-Date).AddDays(90);
$today = (Get-Date)
try{
$expiringsoon = Get-ADUser -Filter * -SearchBase "OU=Powershell Test OU,DC=domain,DC=com" -Properties accountExpires | Where-Object {$_.accountExpires -ne $NeverExpires -and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -lt $ExpireMax -and [datetime]::FromFileTime([int64]::Parse($_.accountExpires)) -gt $ExpireMin }
}catch{
throw $_
}
foreach($user in $expiringsoon){
try{
$userDetails = Get-ADUser $user -Properties AccountExpirationDate,accountExpires,manager
}catch{
Write-Host "Error while searching for user '$user'" -ForegroundColor Red
}
$ExpiryDate = $userDetails.AccountExpirationDate
$DaysLeft = ($ExpiryDate-$today).days
$DateStr = $ExpiryDate.AddDays(-1).ToString("dd/MM/yyyy")
$name = $user.Name
$manager = $userDetails.manager
try{
$managerEmail = (Get-ADUser $manager -Properties emailAddress).emailAddress
if(!$managerEmail){
$managerEmail = $MyEmail
}
}catch{
$managerEmail = $MyEmail
}
# Set Greeting based on Number of Days to Expiry.
# Check Number of Days to Expiry
$messageDays = $DaysLeft
if (($DaysLeft) -gt "1")
{
$messageDays = "in " + "$DaysLeft" + " days"
}
ElseIf (($DaysLeft) -eq "1")
{
$messageDays = "in 1 day"
}
else
{
$messageDays = "today"
}
# Email Subject Set Here
$subject="The account of $name will expire $messageDays "
# Email Body Set Here, Note You can use HTML, including Images.
$body ="
Hi,
<p>the following account '$name' is due to expire, please reply to this email if you wish to extend the account for a further 3 months<br>
<p>Kind regards, <br>
Tech Team
</P>"
#----- Show Details of sent mail -----#
try{
Send-MailMessage -to $managerEmail -from $MyEmail -SmtpServer $SMTP -subject $subject -body $body -bodyasHTML -priority High –Credential $cred -port $SMTPPort -UseSsl
Write-Host "An e-mail has been send to $managerEmail about $name" -ForegroundColor Green
}catch{
Write-Host "Email to $managerEmail for $name failed with error: $($_.exception.message)" -ForegroundColor Red
}
}
Okay, because I couldn't help but wanting to get the script right. Here my present to you. A perfectly working script that sends emails to managers of whom the user expires within 90 days. I also included error handling. Please take this script as a guide to create your own and if you have any questions, feel free.

I am trying to create a script in powershell that will send a email based off of results of a command and have no idea where to start

This is all in powershell 4.0
We currently have this script that we manually run once a week that tells us all the users on the network who's password will expire in <=7 days.
#get max password age policy
$maxPwdAge=(Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
#expiring in 7 days
$7days=(get-date).AddDays(7-$maxPwdAge).ToShortDateString()
Get-ADUser -filter {Enabled -eq $True -and PasswordNeverExpires -eq $False - and PasswordLastSet -gt 0} –Properties * | where {($_.PasswordLastSet).ToShortDateString() -le $7days} | Select-Object -Property "Displayname"
This displays a list of names when run. I then want to take the list of names it generates, add the string "#example.com" to have a string "user.name#example.com"
I thien want to send an email to that email address
$EmailFrom = "noreply#domain.com"
$EmailTo = "destinyemail#domain.com"
The EmailTo needs to change for each person. It could even be emailed to all the users the above outputs in one email.
$EmailSubject = "PasswordExpiring"
$emailbody = " body message "
$SMTPServer = "smtpserver.company.com"
Send-MailMessage -Port 587 -SmtpServer $SMTPServer -From $EmailFrom -To $EmailTo -Attachments $emailattachment -Subject $EmailSubject -Body $emailbody -Bodyashtml;
Assuming that Displayname format is "FirstName LastName" (e.g. "Jon Doe") and email address format is "FirstName.LastName#example.com":
$displayNames = Get-ADUser ...
foreach ($sdisplayName in $displayNames ) {
$Parts = $sdisplayName -split ' '
$EmailTo = $Parts[0] + '.' + $Parts[-1]
Send-MailMessage -To $EmailTo ...
}

PowerShell to send email

I am running a Task Scheduler to execute my PowerShell every 30 minutes.
So
$users = Search-ADAccount -LockedOut -SearchBase
"OU=People,DC=Example,DC=com" -SearchScope Subtree | Select
SamAccountName
This returns a list of users. Now I want to create an emailbody with the users
$EmailBody = ForEach($user in $users) {"`r`n",$user}
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $EmailBody -From $from -Credential $cred -SmtpServer $server -Debug
However my current code will send the email even if NO ONE is locked. Is there anyway I can check to see if the $users has at least 1 person?
(I haven't tested the $EmailBody but would like to know if this is acceptable for BodyAsHtml)
================
Updated Code
if ($users) {
if ($users.count -gt 0) {#even if there are users, this line always is false.
# send the email here
foreach($user in $users)
{
$message = $message + " " + $user + " is locked out" + "`r`n"
Write-Host $user
}
Send-MailMessage -To $to -Subject "Locked Accounts" -BodyAsHtml $message -From $from -Credential $cred -SmtpServer $server -Debug
} }
if ($users -ne $null) # lists can be empty as well, in which case the count fails
{
if ($users.count -gt 0) {
# send the email here
}
}