How to monitor list of services on remote machines? - powershell

I would like to monitor a list of services on remote machines.
These services are not the same on all remote machines.
The closest I have got is to monitor all services which are stopped on remote machines but I cant seem to find a way to script a way to monitor a list of services.
This is script I am working on:
$Date = Get-Date -Format dd-MMM-yyyy
$Time = Get-Date -Format "hh:mm:ss tt"
$Style = #"
<!DOCTYPE html>
<html>
...
"#
$ServerList = Get-Content -Path C:\temp\computers1.txt
$body = $ServerList |
Foreach-Object {
Get-Service -ComputerName $_ | Where-Object {
$_.Status -ne "Running" -and
$_.StartType -like "Automatic"
}
} |
Select-Object MachineName, Status, DisplayName, StartType |
Sort-Object -Property MachineNAme -Descending |
ConvertTo-Html
$colorTagTable = #{
Stopped = ' bgcolor="#ff0000">Stopped<';
Running = ' bgcolor="#00ff00">Running<'
}
# get possible values look them in text sorrounded by > < and replace
# them with style (pun intended).
$colorTagTable.Keys | foreach {
$body = $body -replace ">$_<", ($colorTagTable.$_)
}
ConvertTo-Html -Head $Style -Body $body | Out-File "C:\temp\srv.htm"

When in doubt, read the documentation.
-ComputerName<String[]>
Gets the services running on the specified computers. The default is the local computer.
Type the NetBIOS name, an IP address, or a fully qualified domain name (FQDN) of a remote computer. To specify the local computer, type the computer name, a dot (.), or localhost.
[...]
-Name<String[]>
Specifies the service names of services to be retrieved. Wildcards are permitted. By default, this cmdlet gets all of the services on the computer.
$Style = #"
<style>
...
</style>
"#
$ServiceList = 'NetLogon', 'Spooler', 'W32Time'
$ServerList = Get-Content -Path C:\temp\computers1.txt
Get-Service -ComputerName $ServerList -Name $ServiceList |
Select-Object MachineName, Status, DisplayName, StartType |
Sort-Object -Property MachineNAme -Descending |
ConvertTo-Html -Head $Style |
Out-File 'C:\temp\srv.htm'
Services that don't exist on a particular computer are ignored unless none of the services is running on that computer, in which case you'll get an error. Run Get-Service with the parameter -ErrorAction SilentlyContinue if you want to ignore it.

for service list I will have something like this to actually get the service which should be running and have email reports instead of logging and checking html pages
$servicelist=Get-WmiObject -Class Win32_Service -Filter "state = 'stopped' and startmode = 'auto'" | select Name
$From = "YourEmail#gmail.com"
$To = "AnotherEmail#YourDomain.com"
$Subject = "Daily Service Report From "
$Body = "get-content C:\temp\srv.htm "
$SMTPServer = "smtp.email.com"
#$SMTPPort = "587"
Send-MailMessage -From $From -to $To -Subject $Subject `
-Body $Body -SmtpServer $SMTPServer #-port $SMTPPort

Related

Get email Address from CSV file line and send email to each of them separately

I have a csv file, i want to send email separately to each user, currently from my script i am getting emails to all users addresses those are in my csv file "user" column. Here is my CSV file Data.
#TYPE System.Management.Automation.PSCustomObject
"time","remip","user","msg"
"11:12:15","124.29.228.164","abc#xyz.com.pk","SSL tunnel shutdown"
"11:12:43","124.29.228.164","efg#gmail.com","SSL tunnel established"
"11:13:25","124.29.228.164","abc#xyz.com.pk","SSL tunnel established"
"11:14:05","202.63.194.8","efg#gmail.com","SSL tunnel established"
This is my powershell code
$Subject = " Alerts $(get-date -Format "dddd MM-dd-yyyy HH:mm")"
$Server = "qs42.xyz.com"
$From = "logalerts#abc.com"
$To = $ID = Import-Csv myfile.txt | Select-Object -ExpandProperty user -Unique
$PWD = ConvertTo-SecureString "test123" -AsPlainText -force
$Cred = New-Object System.Management.Automation.PSCredential("logalerts#abc.com" , $pwd)
$path = "myfile.txt"
$file = (Import-Csv $path) | Select-Object -Unique -Property Time , remip , user , Msg
Send-MailMessage -From $from -to $To -Port 587 -SmtpServer $server -Subject $Subject -Credential $cred -UseSsl -Body ($file | Out-String)
Iam able to get emails of this data on both address abc#xyz.com, efg#gmail.com which i dont want, i know emails are receiving because of my variable setting but my requirement is to get emails of each user data on the same user email address.
>>time remip user msg
---- ----- ---- ---
11:12:15 124.29.228.164 abc#xyz.com SSL tunnel shutdown
11:12:59 124.29.228.164 efg#gmail.com SSL tunnel shutdown
11:13:25 124.29.228.164 abc#xyz.com SSL tunnel established
11:14:05 202.63.194.8 efg#gmail.com SSL tunnel established
I don't know how to do this any help please.
There were a couple of things wrong with your code:
$PWD is an automatic variable and means the current working directory. You must not use that name as self-defined variable
You use Import-Csv multiple times where once is enough
The way you try to get a value in the $To variable is wrong
you need Group-Object because some users may have more than one message
I'd recommend using Splatting the parameters to the Send-MailMessage cmdlet
Try:
$password = ConvertTo-SecureString "test123" -AsPlainText -force
$cred = New-Object System.Management.Automation.PSCredential("logalerts#abc.com" , $password)
# import the data from the csv, group on field 'user' (which is the emailaddress to send to)
Import-Csv -Path 'D:\Test\myfile.txt' | Group-Object user | ForEach-Object {
# build a Hashtable to neatly send the parameters to Send-MailMessage cmdlet
# this is called Splatting.
$mailParams = #{
From = 'logalerts#abc.com'
To = $_.Name # the Name of the group is from the user field in the csv
Port = 587
SmtpServer = 'qs42.xyz.com'
Subject = 'Alerts {0:dddd MM-dd-yyyy HH:mm}' -f (Get-Date)
Credential = $cred
UseSsl = $true
Body = $_.Group | Format-Table -AutoSize | Out-String
}
Send-MailMessage #mailParams
}
This is very similar to the answer given by #theo, but it doesn't use the Group-Object cmdlet. I've had problems with memory consumption when the data set is very large because it stores structures (PSObject or PSCustomObjects) within objects and the objects tend to be pretty large.
$PassWord = ConvertTo-SecureString "test123" -AsPlainText -force
$Cred = New-Object System.Management.Automation.PSCredential("logalerts#abc.com"
, $PassWord)
$path = "myfile.txt"
# Get all alerts
$Alerts = Import-Csv $path | Select-Object -Unique -Property Time , remip , user
, Msg
# get individual recipient addresses from the log file
$UniqueUsers = $Alerts |
Select-Object -ExpandProperty user -Unique
ForEach ($User in $UniqueUsers){
$UniqueAlerts = $Alerts |
Where-Object {$_.user -eq $User}
$params = #{
From = 'logalerts#abc.com'
To = $User
Subject = 'Alerts {0:dddd MM-dd-yyyy HH:mm}' -f (Get-Date)
Body = $UniqueAlerts | Out-String # This probably won't align
the
columns properly
# if a mono-spaced font isn't
used by the e-mail client.
# you might consider sending the
message in HTMP format
SmtpServer = 'qs42.xyz.com'
Port = 587
UseSsl = $true
Credential = $cred
}
Send-MailMessage #params
}
As noted in comments, the body of the message will probably be hard to read if it's in plain-text. A HTML message with the data within a table will produces a much easier to read message.

How can I use Powershell to find when an SSL certificate expires for ONLY IIS for a list of servers from OU?

I have this section of code that if I can merely get the script to ONLY reply with Subject that exists (which indicates the IIS cert), then I can be done... (I have the OU enumeration, and the Invoke section down, and the email of the file for scheduling in a task):
[NOTE: I have the expiration set to 500 days so I can then use the script later to merely find specific expiration times]
[NOTE2: $day is set in my $profile to '$day = Get-Date -Format yyyyMMdd']
$serverlist = $serverListpath.Name
foreach($server in $serverlist){
if($server -like '#*')
{
continue
}
$threshold = 500 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
$p = ($c++/$server.count) * 100
Write-Progress -Activity "Checking $._" -Status "$p % completed" -PercentComplete $p;
if(Test-Connection -ComputerName $server -Count 2 -Quiet){
#$server = "KnownIISServerHostname" #<-- to test with a hostname
Invoke-Command -Verbose -ComputerName $server { Dir Cert:\LocalMachine\My } |`
foreach {
If ($_.NotAfter -le $deadline) {
$_ | Select *| select PSComputerName, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}|`
select PSComputerName,Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} |`
export-csv -Force -Append -Encoding ASCII -NoTypeInformation .\output\$day-ExpiringIISSSLCerts.csv
}
}
So where do I tweak this to get the reply to ONLY have existing "Subject" fields; Not to get the null subject field replies (which are RDP certificates)
Try to use this:
Import-Module WebAdministration
$CertAll=Get-ChildItem -Path Cert:\LocalMachine\My
$CertInUse=Get-Childitem -Path IIS:\SslBindings
$CertSame=Compare-Object -ReferenceObject $CertAll -DifferenceObject $CertInUse -Property ThumbPrint -IncludeEqual -ExcludeDifferent
$CertSame | foreach{Get-Childitem –path Cert:\LocalMachine\My\$($_.thumbprint)} | Select-Object -Property Subject, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}}
Since IIS certificates are your scope of concern here, I would suggest using the IIS PowerShell module to make sure you're selecting only certificates that are actually in use by IIS.
The following should pull certificates attached to sites with HTTPS(SSL). I don't currently have multiple sites on a single IIS server for testing, but theoretically this should find all of them, not just the "Default Web Site."
$serverlist = $serverListpath.Name
foreach($server in $serverlist){
if($server -like '#*')
{
continue
}
$threshold = 500 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
$p = ($c++/$server.count) * 100
Write-Progress -Activity "Checking $._" -Status "$p % completed" -PercentComplete $p;
if(Test-Connection -ComputerName $server -Count 2 -Quiet){
#$server = "KnownIISServerHostname" #<-- to test with a hostname
#Pull certificates from existing IIS bindings
$certificates = Invoke-Command -Verbose -ComputerName $server {
Import-Module IISAdministration
$sitebindings = Get-IISSite | foreach { Get-IISSiteBinding -Protocol HTTPS -Name $_ }
$thumbprints = $sitebindings.Attributes | where {$_.Name -match "certificateHash"} | Select-Object -ExpandProperty Value
$thumbprints | foreach {dir Cert:\LocalMachine\My\$_}
}
$certificates |`
foreach {
If ($_.NotAfter -le $deadline) {
$_ | Select *| select PSComputerName, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}|`
select PSComputerName,Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} |`
export-csv -Force -Append -Encoding ASCII -NoTypeInformation .\output\$day-ExpiringIISSSLCerts.csv
}
}
#Complete LOCAL run script. Call this in a Foreach Invoke-command.
$CertAll=GCI -Path Cert:\LocalMachine\My
$CertInUse= (GCI IIS:SSLBindings)
$CertSame=Compare-Object -ReferenceObject $CertAll -DifferenceObject $CertInUse -Property ThumbPrint -IncludeEqual -ExcludeDifferent
#$CertSame=Compare-Object -ReferenceObject $CertAll -Property ThumbPrint -IncludeEqual -ExcludeDifferent
$CertSame | foreach{GCI -filter "" –path Cert:\LocalMachine\My\$($_.thumbprint)} | Select-Object -Property Issuer, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}} -First 1
Thank you to #bruce-zhang
Similar to #bruce-zhangs's excellent answer but gets the certs in use first, then retrieves only those from the appropriate certificate stores (instead of only looking at the My cert store):
Import-Module WebAdministration
$CertsInUse = Get-Childitem -Path IIS:\SslBindings
$CertsInUse | foreach{Get-Childitem –path Cert:\LocalMachine\$($_.Store)\$($_.Thumbprint)} | Select-Object -Property FriendlyName,Subject, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}}
Here it is with a more verbose foreach:
Import-Module WebAdministration
$CertsInUse = Get-Childitem -Path IIS:\SslBindings
$CertsDetails = #()
foreach ($Cert in $CertsInUse) {
$CertsDetails += Get-ChildItem -Path Cert:\LocalMachine\$($Cert.Store)\$($Cert.Thumbprint)
}
$CertsDetails | Select-Object -Property FriendlyName,Subject, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}}
#checkCertExpDate-manual.ps1
$day = Get-Date -Format yyyyMMdd
$threshold = 5000 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
Dir Cert:\LocalMachine\My | foreach {
If ($_.NotAfter -le $deadline) { $_ | Select Issuer, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}
Then you just grep for the name:
.\checkCertExpDate-manual.ps1|Select-String -pattern "companyname"
Now, I can set the '$threshold' to whatever I want...
I invoke this remotely, after I copied to every server, and wrote the output to a log I then email to myself automatically every week from a scheduled task.
#D:\batch\checkCertExpDate.ps1
$day = Get-Date -Format yyyyMMdd
Set-Location d:\batch
$serverlist = gc ".\serverlist.txt"
foreach($server in $serverlist)
{
$threshold = 45 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
Invoke-Command $server { Dir Cert:\LocalMachine\My } | foreach {
If ($_.NotAfter -le $deadline) { $_ | Select Issuer, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}|select -expandproperty Subject|out-file .\output\$day-ExpiringIISSSLCerts.txt -Encoding ascii -Append
}
# Start mail send
$log = "d:\batch\output\$day-ExpiringIISSSLCerts.txt"
if(Test-Path -Path $log){
$smtpServer = "smtp.domain.com"
$messageSubject = "Verify SSL Cert Check Report - " + $env:computername
$message = New-Object System.Net.Mail.MailMessage
$message.From = "authorizedaccount#domain.com"
$message.To.Add("patrick.burwell#domain.com")
$message.Subject = $messageSubject
$message.IsBodyHTML = $true
$message.Body = "<head><pre>$style</pre></head>"
$message.Body += "Cert Check Report - " + $env:computername
$message.Body += Get-Date
$message.Body += "<br><b>Expiring Non-Prod Verify SSL Certificates Report from " + $env:computername + "</b>"
$message.Attachments.Add($log)
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)
}
$result = Get-content $log
write-host $result |format-list -View table

How to read input through a csv file and write the output in an output file in power shell script?

[This is the powershell script to get the selected services status of servers,where list of servers are given through input csv file and the status of those server should be stored in an output file.
-----------Below is the script----------
$Servers = Get-Content "C:\temp\input.csv"
$Log = #()
foreach($Server in $Servers)
{
$Services = Get-Service *HRRA*, "SQL Server Reporting Services" -ComputerName $COMPUTERNAME
foreach ($Service in $Services)
{
$properties = #(
#{n='ServiceName';e={$Service.Name}}
#{n='Status';e={$Service.Status}}
#{n='ServerName';e={$Server}}
)
$Log += "" | select $properties
}
}
$Log | Format-Table -AutoSize | Out-File "D:\temp\test.txt" -Force
------------------------------------New Script----------------------------------
$Computers = Import-Csv "C:\Temp\Input.csv"
#$mailboxdata = Get-Service *HRRA*,"SQL Server Reporting Services" -ComputerName $ComputerName| select machinename,name, status | sort machinename |
#format-table -AutoSize |Out-File "D:\Temp\RRR.txt"
#LogWrite "$ComputerName"
foreach($row in $computers)
{
{
Add-Content -Path D:\Temp\SSRS.txt -Value $mailboxdata }
Get-Content -Path D:\Temp\SSRS.txt
$ComputerName= $row.ComputerName;
$mailboxdata = Get-Service *HRRA*,"SQL Server Reporting Services" -ComputerName $ComputerName| select machinename,name, status | sort machinename |
format-table -AutoSize |Out-File "D:\Temp\SSR.txt"
$fromaddress = "Reporting.Services#accenture.com"
$toaddress = "aditi.m.singh#accenture.Com"
#$toaddress1 = "s.ab.balakrishnan#accenture.com"
$Subject = "SSRS Services Status"
$body = "Please find attached - test"
$attachment = "D:\Temp\SSR.txt"
$smtpserver = "AMRINT.SMTP.ACCENTURE.COM"
$message = new-object System.Net.Mail.MailMessage
$message.From = $fromaddress
$message.To.Add($toaddress)
#$message.To.Add($toaddress1)
$message.IsBodyHtml = $True
$message.Subject = $Subject
$attach = new-object Net.Mail.Attachment($attachment)
$message.Attachments.Add($attach)
$message.body = $body
$smtp = new-object Net.Mail.SmtpClient($smtpserver)
$smtp.Send($message)
}
If i am running the script with static value its giving me output for both the servers---Below is the script----
Get-Service *HRRA*,"SQL Server Reporting Services" -ComputerName VW118627, VW118623 | select name, status, machinename | sort machinename | format-table -AutoSize |
Out-File "D:\Temp\Report.txt"
Looking at the screenshot, I can see the input csv is really a Comma Separated Values file. For these, you use the Import-Csv cmdlet to retrieve an array of computer names from the file.
$outputFile = "D:\temp\test.csv"
# make sure the field name matches what is in the CSV header
$Servers = (Import-Csv -Path "C:\temp\input.csv").ComputerName
$Log = foreach($Server in $Servers) {
# add a test to see if we can reach the server
if (Test-Connection -ComputerName $Server -Count 1 -Quiet -ErrorAction SilentlyContinue) {
Get-Service -Name *HRRA*, "SQL Server Reporting Services" -ComputerName $Server |
Select-Object #{Name = 'MachineName'; Expression = {$Server}},
#{Name = 'ServiceName'; Expression = {$_.Name}},
#{Name = 'Status'; Expression = {$_.Status}}
}
else {
Write-Warning "Server '$Server' is unreachable"
}
}
# if you want the log sorted, do
$Log = $Log | Sort-Object MachineName
# output on screen
$Log | Format-Table -AutoSize
# output to CSV file
$Log | Export-Csv -Path $outputFile -Force -NoTypeInformation
# mail the output csv file using Send-MailMessage
# collect the parameters in a hashtable
$mailParams = #{
SmtpServer = "AMRINT.SMTP.ACCENTURE.COM"
From = "Reporting.Services#accenture.com"
To = "aditi.m.singh#accenture.com"
Subject = "SSRS Services Status"
Body = "Please find attached - test"
BodyAsHtml = $true
Attachments = $outputFile
# add other parameters if needed
# see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage
}
# use 'splatting' to send the email
# see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting
Send-MailMessage #mailParams
P.S. The Format-Table cmdlet is for displaying the object(s) on screen only.

Sending foreach output via email

I have a script that is checking mirror status of databases. Output in Powershell is fine, but when I try to send it via mail, I'm getting "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData" instead of data itself. I've tried to change it to Out-String but then I'm getting all results in one line. How this could be done to have formated output the same way as it is formated directly in PowerShell?
# rozszerzenie do obslugi
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null;
$mail_from = "xxx";
$mail_to = "xxx";
$mail_subject = "Status mirrorowanych baz";
$mail_encoding = "UTF8";
$mail_smtp = "xxx";
# lista serwerow
$list = #("SERVER01V",
"SERVER02V"
);
$output = foreach($server in $list)
{
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $server;
# pokaz tylko mirrorowane
$databases = $srv.Databases | Where-Object {$_.IsMirroringEnabled -eq $true};
Write-Output "<br>==================================<br>";
Write-Output $server;
Write-Output "<br>==================================<br>";
$databases | Select-Object -Property Name, MirroringStatus | Format-Table -AutoSize | Out-String;
Write-Output "<br>";
}
$mail_body = $output;
Send-MailMessage -To $mail_to -From $mail_from -Subject $mail_subject -SmtpServer $mail_smtp -Encoding $mail_encoding -Body $mail_body -BodyAsHtml
You're currently sending a HTML mail. As such line breaks won't matter. If you want line breaks in your mail you either need to use text format or replace line breaks with a <br /> or something similar. It's probably going to be wise to have manually add <br /> in your look to have a consistent pattern and replace.
Try gather all data into an array, then use ConvertTo-HTML cmdlet, and the 'BodyAsHTML' switch in Send-MailMessage
$DatabaseArray=#()
ForEach ($server in $list) {
$srv = New-Object "Microsoft.SqlServer.Management.Smo.Server" $server
$DatabaseArray += $srv.Databases | Where-Object {$_.IsMirroringEnabled -eq $true} | Select-Object Name,MirroringStatus
}
$HTMLBody = $DatabaseArray | ConvertTo-HTML
Send-MailMessage -subject x -body $HTMLBody -BodyAsHTML

Powershell Script to Disable Inactive AD Users Create Log and Send E-mail

A colleague has reached out to me to create a PowerShell script to do the following:
The script would read the lastlogondate of an AD security group called “Temp Associates”, disable the accounts with lastlogondate > or = 29 days from current date and move to Disabled OU. When it disables it will also change the descripton to the date it was disabled on. Then create a report listing disabled users and email to our global helpdesk.
I've compiled some things together that seem like they should work, but do not. When I run the script I receive no error message and the log file is generated with no data populated. In order to remain SOX compliant I should be able to manipultate the value in $PasswordAge = (Get-Date).adddays(-29) for testing purposes as I'm not sure we have any accounts that meet the requirements currently.
E-mail is working now, just had to create PSCredential to use in send-mailmessage -credential parameter.
I am definitley new to PowerShell and can use all the help I can get. Any suggestions to either improve the existing code or use a different method are welcome, but I'd like to utilize what I already have if possible.
Code Below:
#import the ActiveDirectory Module
Import-Module ActiveDirectory
#Create a variable for the date stamp in the log file
$LogDate = get-date -f yyyyMMddhhmm
#Sets the OU to do the base search for all user accounts, change for your env.
$SearchBase = "CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org"
#Create an empty array for the log file
$LogArray = #()
#Sets the number of days to disable user accounts based on lastlogontimestamp and pwdlastset.
$PasswordAge = (Get-Date).adddays(-29)
#Use ForEach to loop through all users with pwdlastset and lastlogontimestamp greater than date set. Also added users with no lastlogon date set. Disables the accounts and adds to log array.
#Add the properties you will be using to ensure they are available.
$DisabledUsers = (Get-ADUser -searchbase $SearchBase -Properties samaccountname, name, distinguishedname -filter {((lastlogondate -notlike "*") -OR (lastlogondate -le $Passwordage)) -AND (enabled -eq $True) -AND (whencreated -le $Passwordage)} )
if ($DisabledUsers -ne $null -and $DisabledUsers.Count > 0) {
ForEach ($DisabledUser in $DisabledUsers) {
#Sets the user objects description attribute to a date stamp. Example "11/13/2011"
set-aduser $DisabledUser -Description ((get-date).toshortdatestring()) -whatif
#Disabled user object. To log only add "-whatif"
Disable-ADAccount $DisabledUser -whatif
#Create new object for logging
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $DisabledUser.name
$obj | Add-Member -MemberType NoteProperty -Name "samAccountName" -Value $DisabledUser.samaccountname
$obj | Add-Member -MemberType NoteProperty -Name "DistinguishedName" -Value $DisabledUser.DistinguishedName
$obj | Add-Member -MemberType NoteProperty -Name "Status" -Value 'Disabled'
#Adds object to the log array
$LogArray += $obj
}
# Move disabled users in Temp Associates group to Disabled OU
Search-ADAccount –AccountDisabled –UsersOnly –SearchBase “CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org” |
Move-ADObject –TargetPath “OU=Disabled,DC=our,DC=domain,DC=org” -WhatIf
#Exports log array to CSV file in the temp directory with a date and time stamp in the file name.
$logArray | Export-Csv "C:\Temp\User_Report_$logDate.csv" -NoTypeInformation
#Create PSCredential for use in e-mail -credential parameter
$secpasswd = ConvertTo-SecureString "PasswordHere" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("UserHere", $secpasswd)
#Send e-mail to Global Helpdesk with report generated
$emailFrom = "smtp#address.com"
$emailTo = "User#address.com"
$subject = "NA Disabled Temp Users to be deleted"
$smtpServer = "smtp.address.com"
$attachment = "C:\Temp\User_Report_$logDate.csv"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -SmtpServer $smtpServer -attachment $attachment -credential $mycreds
}else {
Write-Output "No disabled users to process for $PasswordAge."
#Create PSCredential for use in e-mail -credential parameter
$secpasswd = ConvertTo-SecureString "PasswordHere" -AsPlainText -Force
$mycreds = New-Object System.Management.Automation.PSCredential ("UserHere", $secpasswd)
#Send e-mail to Global Helpdesk with report generated
$emailFrom = "smtp#address.com"
$emailTo = "User#address.com"
$subject = "NA Disabled Temp Users to be deleted"
$smtpServer = "smtp.address.com"
$attachment = "C:\Temp\User_Report_$logDate.csv"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -Body "No disabled users to process for $PasswordAge." -SmtpServer $smtpServer -credential $mycreds
}
Putting it as an answer, even though it is not a direct answer.
It is really hard to say what is wrong especially when you are not implementing any checks along the way. A basic debugging strategy would be to add a few outputs along the way to see if the script is hitting sections. Such was: write-output "Entering Foreach" and write-output "Looping user $($DisabledUser.samaccountname)" to ensure that your script is executing properly. This will help determine where your hiccup is.
Alternatively, where I would first look is in your Get-ADUser query. Run that alone and make sure it returns users. If not get it to where it returns expected results.
Here is a revised version of your code that has an error check if there are no users returned.
#import the ActiveDirectory Module
Import-Module ActiveDirectory
#Create a variable for the date stamp in the log file
$LogDate = get-date -f yyyyMMddhhmm
#Sets the OU to do the base search for all user accounts, change for your env.
$SearchBase = "CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org"
#Create an empty array for the log file
$LogArray = #()
#Sets the number of days to disable user accounts based on lastlogontimestamp and pwdlastset.
$PasswordAge = (Get-Date).adddays(-29)
#Use ForEach to loop through all users with pwdlastset and lastlogontimestamp greater than date set. Also added users with no lastlogon date set. Disables the accounts and adds to log array.
#Add the properties you will be using to ensure they are available.
$DisabledUsers = (Get-ADUser -searchbase $SearchBase -Properties samaccountname, name, distinguishedname -filter {((lastlogondate -notlike "*") -OR (lastlogondate -le $Passwordage)) -AND (enabled -eq $True) -AND (whencreated -le $Passwordage)} )
if ($DisabledUsers -ne $null -and $DisabledUsers.Count > 0) {
ForEach ($DisabledUser in $DisabledUsers) {
#Sets the user objects description attribute to a date stamp. Example "11/13/2011"
set-aduser $DisabledUser -Description ((get-date).toshortdatestring()) -whatif
#Disabled user object. To log only add "-whatif"
Disable-ADAccount $DisabledUser -whatif
#Create new object for logging
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $DisabledUser.name
$obj | Add-Member -MemberType NoteProperty -Name "samAccountName" -Value $DisabledUser.samaccountname
$obj | Add-Member -MemberType NoteProperty -Name "DistinguishedName" -Value $DisabledUser.DistinguishedName
$obj | Add-Member -MemberType NoteProperty -Name "Status" -Value 'Disabled'
#Adds object to the log array
$LogArray += $obj
}
# Move disabled users in Temp Associates group to Disabled OU
Search-ADAccount –AccountDisabled –UsersOnly –SearchBase “CN=Temp Associates,OU=Res Accounts,DC=our,DC=domain,DC=org” |
Move-ADObject –TargetPath “OU=Disabled,DC=our,DC=domain,DC=org” -WhatIf
#Exports log array to CSV file in the temp directory with a date and time stamp in the file name.
$logArray | Export-Csv "C:\Temp\User_Report_$logDate.csv" -NoTypeInformation
#Send e-mail to Global Helpdesk with report generated
$emailFrom = "sender#mail.com"
$emailTo = "recipient#mail.com"
$subject = "NA Disabled Temp Users to be deleted"
$smtpServer = "smtp.server.com"
$attachment = "C:\Temp\User_Report_$logDate.csv"
Send-MailMessage -To $emailTo -From $emailFrom -Subject $subject -SmtpServer $smtpServer -attachment $attachment
}else {
Write-Output "No disabled users to process for $PasswordAge."
}
i found that the code in the if is never executed.
You must replace $DisabledUsers.Count > 0 with $DisabledUsers.Count -gt 0