On my venture in learning PowerShell, I have hit into another issue which I would like to get your help in this.
I have a code below inside a try-catch but when an error occurs inside the try block it does not seem to send it to catch block.
The Code:
try{
if ($isFileAvailable -gt 0){
$hashArgumentWithAttachment = #{
From = $From
To = $To
Subject = $Subject
Body = $Body
SmtpServer = $SMTPServer
Port = $SMTPPort
UseSsl = $true
Credential = $smtpCredential
Attachments = $filePath
}
Send-MailMessage #hashArgumentWithAttachment
}
Else {
$hashArgumentWithOutAttachment = #{
From = $From
To = $To
Subject = $Subject
Body = $Body
SmtpServer = $SMTPServer
Port = $SMTPPort
UseSsl = $true
Credential = $smtpCredential
}
Send-MailMessage #hashArgumentWithoutAttachment
}
Write-Output "Emailed"
break
}catch {
Write-Output "Error Occured, No of Attempts So far: " + $attempts.ToString()
}
On an instance where there is a Server connectivity error I expect to hit the catch block but instead it throws an error like this:
Add ErrrorAction to the CMDLet:
Send-MailMessage #hashArgumentWithoutAttachment -ErrorAction Stop
Cheers!
Related
I found a tutorial to send an email when a certain event occurs. The code is this:
$EventId = 4771
$A = Get-WinEvent -MaxEvents 1 -FilterHashTable #{Logname = "System" ; ID = $EventId}
$Message = $A.Message
$EventID = $A.Id
$MachineName = $A.MachineName
$Source = $A.ProviderName
$EmailFrom = "email#domain.com"
$EmailTo = "email#domain.com"
$Subject ="Alert From $MachineQ"
$Body = "EventID: $EventIDnSource: $SourcenMachineQ: $MachineQ `nMessage: $Message"
$SMTPServer = "smtp.server"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
The problem is that I would also like to receive the date/time of the event in the email but I don't know how to add that parameter. Currently the email looks like this:
EventID: 4771
Source: Microsoft-Windows-Security-Auditing
MachineName: server.domain
Message: user blocked.
I would like something like:
EventID: 4771
Source: Microsoft-Windows-Security-Auditing
**Date: 25/11/2022 10:02**
MachineName: server.domain
Message: user blocked.
Could someone tell me how to modify the code to add this parameter?
I am trying to obtain the difference in time between each start and stop action in the following output:
User Computer Time Event Action
---- -------- ---- ----- ------
TEST\me TEST-DC-01 27/05/2020 14:45:06 Logoff Stop
TEST\me TEST-DC-01 27/05/2020 14:44:56 Unlock Start
TEST\me TEST-DC-01 27/05/2020 14:44:49 Lock Stop
TEST\me TEST-DC-01 27/05/2020 14:44:40 Reconnect Start
TEST\me TEST-DC-01 27/05/2020 14:43:17 Disconnect Stop
TEST\me TEST-DC-01 27/05/2020 14:43:07 Logon Start
I've been playing with the New-Timespan cmdlet but seem to be going round and round in circles when trying to integrate it with the script (below) which the above output is derived from.
Ideally, I'd like to see the following appended to it:
Total active time: 00:00:29
Can anyone advise on the best approach (either by fully integrating with the existing script or by importing the output in CSV format).
Any guidance most appreciated.
#
# GROUP POLICY (TO ENABLE 480x AND 477x EVENTS):
#
# COMPUTER CONFIGURATION
# - WINDOWS SETTINGS
# - SECURITY SETTINGS
# - ADVANCED AUDIT POLICY CONFIGURATION
# - SYSTEM AUDIT POLICIES - LOCAL GROUP POLICY OBJECT
# - LOGON/LOGOFF
# - AUDIT OTHER LOGON/LOGOFF EVENTS
# - SUCCESS
#
$OU = "OU=Domain Controllers,DC=test,DC=local"
# WHERE TO SEARCH FOR COMPUTERS IN ACTIVE DIRECTORY
$days = "-1"
# HOW MANY DAYS TO SEARCH BACK THROUGH
$directory = "C:\"
# LOCATION OF FAILED CONNECTIONS FILE
$computers = Get-ADComputer -Filter * -SearchBase $OU
$logs = #()
$timestamp = ((Get-Date).ToString("yyyyMMdd_HHmmss"))
$output = ForEach ($computer in $computers){
try{
$logs += get-eventlog `
-LogName system `
-ComputerName $computer.Name `
-After (Get-Date).AddDays($days)
$logs += get-eventlog `
-LogName security `
-ComputerName $computer.Name `
-After (Get-Date).AddDays($days)
$res = #()
ForEach ($log in $logs){
if($log.instanceid -eq 7001){
$type = "Logon"
$user =
try{
(New-Object System.Security.Principal.SecurityIdentifier(
$log.ReplacementStrings[1])).Translate(
[System.Security.Principal.NTAccount]).value
}
catch{
$log.ReplacementStrings[1]
}
$action = "Start"
}
Elseif ($log.instanceid -eq 7002){
$type = "Logoff"
$user =
try{
(New-Object System.Security.Principal.SecurityIdentifier(
$log.ReplacementStrings[1])).Translate(
[System.Security.Principal.NTAccount]).value
}
catch{
$log.ReplacementStrings[1]
}
$action = "Stop"
}
Elseif ($log.instanceid -eq 4800){
$type = "Lock"
$user = $log.ReplacementStrings[2] + "\" +
$log.ReplacementStrings[1]
$action = "Stop"
}
Elseif ($log.instanceid -eq 4801){
$type = "Unlock"
$user = $log.ReplacementStrings[2] + "\" +
$log.ReplacementStrings[1]
$action = "Start"
}
Elseif ($log.instanceid -eq 4778){
$type = "Reconnect"
$user = $log.ReplacementStrings[1] + "\" +
$log.ReplacementStrings[0]
$action = "Start"
}
Elseif ($log.instanceid -eq 4779){
$type = "Disconnect"
$user = $log.ReplacementStrings[1] + "\" +
$log.ReplacementStrings[0]
$action = "Stop"
}
Else {
Continue
}
$hash = [ordered]#{
"User" = $user
"Computer" = $computer.Name
"Time" = $log.TimeWritten
"Event" = $type
"Action" = $action
}
$res += New-Object PSObject -Property $hash
}
$res
}
Catch {
Add-Content -Path "${directory}${timestamp}_failed.txt" $computer.Name
}
}
$TimeDescending = #{
Expression = 'Time'
Descending = $true
}
$EventDescending = #{
Expression = 'Event'
Descending = $true
}
$output |
sort User,Computer,$TimeDescending,$EventDescending |
? User -like "*me" |
ft
You could use the New-TimeSpan cmdlet:
$start = Get-Date
# Do some stuff here
$end = Get-Date
$timeTaken = (New-TimeSpan -Start $start -End $end).TotalSeconds
I have written a ps script which do the the job when i execute it from ISE but when i save it and run it from CMD i have an error,
basicly i have made gui with list of OU and list of GPO and i wanted to make a link and unlink button (eg OU named blabla and GPO named GPOblabla so if i check them both and press link the code would link and unlink would unlink)
the problem is (with my understanding) with the button2 (unlink) function which is defined at the beginning of the code, and in that function im using command that use parameters later in the code, i guess ISE save it in memory? whats the best thing to do here?
here is the code:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$button2_click = {
try {Remove-GPLink -Name $GPO -target $OU}
catch {
Write-Warning $Error[0]
} $form.close() }
$form = New-Object System.Windows.Forms.Form
$form.Text = 'GPO Connector V1.0'
$form.Size = New-Object System.Drawing.Size(600,200)
$form.StartPosition = 'CenterScreen'
$button1 = New-Object System.Windows.Forms.Button
$button1.Location = New-Object System.Drawing.Point(10,120)
$button1.Size = New-Object System.Drawing.Size(75,23)
$button1.Text = 'Link'
$button1.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $button1
$button2 = New-Object System.Windows.Forms.Button
$button2.Location = New-Object System.Drawing.Point(90,120)
$button2.Size = New-Object System.Drawing.Size(75,23)
$button2.Text = 'UnLink'
$Button2.Add_Click($Button2_Click)
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(80,20)
$label.Size = New-Object System.Drawing.Size(480,20)
$label.Text = 'SELECT GPO And Corresponding OU (ONLY WORKSTATION OU)'
$form.Controls.Add($label)
$form.Controls.Add($button1)
$form.Controls.Add($button2)
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(260,20)
$listBox.Height = 80
$listBox2 = New-Object System.Windows.Forms.ListBox
$listBox2.Location = New-Object System.Drawing.Point(300,40)
$listBox2.Size = New-Object System.Drawing.Size(260,20)
$listBox2.Height = 80
#gpolist.txt holding the gpo's and oulist.txt would hold the ou's
Get-Content .\gpolist.txt | ?{$_ -notmatch "^#"} | Where-Object { $_.Trim() -ne '' } | ForEach-Object {
[void] $listBox.Items.Add("$_")}
Get-Content .\oulist.txt | ?{$_ -notmatch "^#"} | Where-Object { $_.Trim() -ne '' } | ForEach-Object {
[void] $listBox2.Items.Add("$_")}
$form.Controls.Add($listBox)
$form.Controls.Add($listBox2)
$form.Topmost = $true
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::Cancel)
{ break }
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$GPO = $listBox.SelectedItem
$OU = $listBox2.SelectedItem }
New-GPLink $GPO -target $OU | out-null }
You're not setting variables $GPO and $OU until after showing the dialog, so these variables won't be defined in your $button2_click script block, so the Remove-GPLink call won't work as expected.
One way to solve the problem is to refer to both $listBox.SelectedItem and $listBox2.SelectedItem directly:
$button2_click = {
try {
Remove-GPLink -Name $listBox.SelectedItem -target $listBox2.SelectedItem
}
catch {
Write-Warning $Error[0]
}
$form.close()
}
Note that if you were to define variables $GPO and $OU inside that script block, you'd have to define them as $script:GPO = ... and script:$OU = ... if you wanted to also access them after closing the dialog.
As for why things worked in the ISE:
The ISE dot-sources your scripts when it runs them, and with repeated invocations variables may linger and affect subsequent runs.
The implication is that you had run at least once with the code path $GPO = $listBox.SelectedItem and $OU = $listBox2.SelectedItem getting hit, which would have made clicking on button 2 in subsequent runs "work".
I need to modify the script below, so I can get the list of AD server and then check for any SSL certificate that is in the server for its validity.
Note: The server may or not may run IIS, which is why I am not sure how to do it properly.
$ComputerName = Get-ADComputer -Filter {Enabled -eq $True} -SearchBase "OU=Servers,OU=Production,DC=Domain,DC=com"
[CmdletBinding()]
param(
[parameter(Mandatory, ValueFromPipeline)][string[]]$ComputerName,
[int]$TCPPort = 443,
[int]$Timeoutms = 3000
)
process {
foreach ($computer in $computerName) {
$port = $TCPPort
write-verbose "$computer`: Connecting on port $port"
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$req = [Net.HttpWebRequest]::Create("https://$computer`:$port/")
$req.Timeout = $Timeoutms
try {$req.GetResponse() | Out-Null} catch {write-error "Couldn't connect to $computer on port $port"; continue}
if (!($req.ServicePoint.Certificate)) {write-error "No Certificate returned on $computer"; continue}
$certinfo = $req.ServicePoint.Certificate
$returnobj = [ordered]#{
ComputerName = $computer;
Port = $port;
Subject = $certinfo.Subject;
Thumbprint = $certinfo.GetCertHashString();
Issuer = $certinfo.Issuer;
SerialNumber = $certinfo.GetSerialNumberString();
Issued = [DateTime]$certinfo.GetEffectiveDateString();
Expires = [DateTime]$certinfo.GetExpirationDateString();
}
new-object PSCustomObject -Property $returnobj
}
}
I'm not sure if you've forgotten to put the function instantiation on top or not, but the following should be the correct format for an advanced function in PowerShell. You can also give the parameter $ComputerName a default value with the Get-ADComputer cmdlet. Try this out to see if this works.
function Get-ADComputerCert {
[CmdletBinding()]
param(
[int]$TCPPort = 443,
[int]$Timeoutms = 3000
)
process {
$ComputerName = (Get-ADComputer -Filter {Enabled -eq $True} -SearchBase "OU=Servers,OU=Production,DC=Domain,DC=com").Name
foreach ($computer in $computerName) {
$port = $TCPPort
write-verbose "$computer`: Connecting on port $port"
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$req = [Net.HttpWebRequest]::Create("https://$computer`:$port/")
$req.Timeout = $Timeoutms
try {$req.GetResponse() | Out-Null} catch {write-error "Couldn't connect to $computer on port $port"; continue}
if (!($req.ServicePoint.Certificate)) {write-error "No Certificate returned on $computer"; continue}
$certinfo = $req.ServicePoint.Certificate
$returnobj = [ordered]#{
ComputerName = $computer;
Port = $port;
Subject = $certinfo.Subject;
Thumbprint = $certinfo.GetCertHashString();
Issuer = $certinfo.Issuer;
SerialNumber = $certinfo.GetSerialNumberString();
Issued = [DateTime]$certinfo.GetEffectiveDateString();
Expires = [DateTime]$certinfo.GetExpirationDateString();
}
new-object PSCustomObject -Property $returnobj
}
}
}
I am completely stumped. I am trying to add an embedded image to a notification email. The only scripts i can find are using .net to create a new email object.Can this only be done using .net to create an email object? Any Ideas
#################################################
# Configure the following variables….
# expireindays1 + 2 = At what count of days left on a password do you want a notification?
$smtpServer=”smtprelay.domain.com”
$expireindays1 = 5
#$expireindays2 = 1
$from = “Technology Helpdesk <technologyhelpdesk#domain.com>”
#################################################
#Get Users From AD who are enabled
Import-Module ActiveDirectory
$users = get-aduser -filter * -Properties enabled, GivenName, passwordneverexpires, passwordexpired, emailaddress, passwordlastset |where {$_.Enabled -eq “True”} | where { $_.PasswordNeverExpires -eq $false } | where { $_.passwordexpired -eq $false }
foreach ($user in $users)
{
$Name = (Get-ADUser $user | foreach { $_.Name})
$UserID = (Get-ADUser $user -Properties *).Samaccountname
$emailaddress = $user.emailaddress
$passwordSetDate = (get-aduser $user -properties passwordlastset | foreach { $_.PasswordLastSet })
$PasswordPol = (Get-AduserResultantPasswordPolicy $user)
# Check for Fine Grained Password
if (($PasswordPol) -ne $null)
{
$maxPasswordAge = ($PasswordPol).MaxPasswordAge
}
else
{
$maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge
}
$expireson = $passwordsetdate + $maxPasswordAge
$today = (get-date)
$daystoexpire = (New-TimeSpan -Start $today -End $Expireson).Days
#$subject=
$body = #'
#<html>
<body>
<img src="cid:image1"><br>
<b>$UserID : Your password will expire soon</b>
<br>
<br>
<br>
Dear $name,<br>
Your password will expire in $daystoexpire days. Please change your password before it expires to avoid password related problems. .<br>
<br>
<br>
<b>Password complexity rules:<br>
<br>
- Must be 7 or 8 characters long<br>
- Must contain at least 1 uppercase letter<br>
- Must contain at least 1 lowercase letter<br>
- Must contain at least 1 number<br>
- Must NOT contain repeating characters (e.g., aa, 11)</b><br>
<br>
<br>
Please use the following steps to change your password:<br>
- Press CTRL+ALT+DEL<br>
- Click Change a password…<br>
- Verify your User ID is correct, then type in your old and new passwords (you cannot use previously used passwords)<br>
- After the change is complete, you will be prompted that your password has been changed<br>
- If you have a Blackberry, iPhone, or iPad, those devices will need your new password as soon as your password has been changed<br>
<br>
If you have questions, or need assistance updating your passwords, please contact the Technology Help Desk. <br>
</body>
#</html>
'#
if (($daystoexpire -eq $expireindays1))
# -or ($daystoexpire -eq $expireindays2))
{
#Send-Mailmessage -smtpServer $smtpServer -from $from -to user#domain.com -subject $subject -body $body -bodyasHTML -priority High
####################################################################################################################
$images = #{
image1 = "c:\temp\action needed.png"
}
$params = #{
InlineAttachments = $images
#Attachments = 'C:\temp\action needed.png'
Body = $body
BodyAsHtml = $true
Subject = ”Your password will expire in $daystoExpire days”
From = "Technology Helpdesk <technologyhelpdesk#domain.com>"
To = 'user#domain.com'
#Cc = 'recipient2#domain.com', 'recipient3#domain.com'
SmtpServer = 'smtprelay.domain.com'
}
Send-MailMessage #params
}
}
This code does not use the dot net classes. I hope it helps.
$images = #{
image1 = 'c:\temp\test.jpg'
image2 = 'C:\temp\test2.png'
}
$body = #'
<html>
<body>
<img src="cid:image1"><br>
<img src="cid:image2">
</body>
</html>
'#
$params = #{
InlineAttachments = $images
Attachments = 'C:\temp\attachment1.txt', 'C:\temp\attachment2.txt'
Body = $body
BodyAsHtml = $true
Subject = 'Test email'
From = 'username#gmail.com'
To = 'recipient#domain.com'
Cc = 'recipient2#domain.com', 'recipient3#domain.com'
SmtpServer = 'smtp.gmail.com'
Port = 587
Credential = (Get-Credential)
UseSsl = $true
}
Send-MailMessage #params
BTW all credit to David Wyatt who created the code. He seems to be a good resource on powershell script.