Author Posts

September 13, 2016 at 2:02 am

Hi All,

I currently have a PowerShell script i have made which provisions a new user account in Active Directory. The script gets to a stage at the end where it asks whether an account expiry date has been requested using the Read-host method.

What i am trying to achieve is if the user running the script gets to this stage and forgets to enter the requested input after say 3 minutes i would like the script to send a Reminder email to the user running the script advising that the script is still running and requires their attention.

I have successfully implemented a timer event which will send the email after 3 minutes but this is without the Read-Host event occurring, when the Read-host occurs waiting for user input the script seems to pause (including the timer) so the reminder email does not get sent.

The code i have in this section of the script is as follows:

$ReminderTimer = New-Object System.Timers.Timer
$ReminderTimer.Interval = 180000

add-content -Path $pathReminder -Value "The New User PowerShell script is still waiting for your input. A Reminder email will be sent to you every 3 minutes until the PowerShell script is attended to."
add-content -Path $pathReminder -Value " "
add-content -Path $pathReminder -Value "Regards"
add-content -Path $pathReminder -Value " "
add-content -Path $pathReminder -Value "HPW Account Provisioning Team"
$fileReminder = Get-Content $pathReminder
$bodyReminder = ($fileReminder | Out-String)
$toReminder = $UserRunningThisScriptEmail
$fromReminder = "HPW-AccountProvisioningTeam@hpw.qld.gov.au"
$SubjectReminder = "REMINDER: New User Provisioning Script Is Awaiting Your Input"
$smtpReminder = "smtp"

$Action = {Send-MailMessage -to $toReminder -Subject $SubjectReminder -body $bodyReminder -smtp $smtpReminder -from $fromReminder}

Register-ObjectEvent -InputObject $ReminderTimer -EventName Elapsed -SourceIdentifier TimerEmail -Action $Action

$ReminderTimer.Enabled = $True
$ReminderTimer.Start()

$AccountExpiry = Read-Host "Has an end date been specified on the attached Form?"

All in all is there a way to have the Read-Host event occur with the timer running in the background sending an email every 3 minutes until user input is provided. If user input is provided then the timer will then be disabled and the script can continue.

Any help would be much appreciated.

Thanks in advance.

Shaun

September 13, 2016 at 6:38 am

Off the top of my head I don't think you will be able to do that using read-host because as you have found out it pauses the script waiting for input. Could you not just move the capture of the expiry information further up the workflow? Make them fill out the account expiry first before getting them to input the rest of the details.

September 13, 2016 at 2:37 pm

There is no timer tick event that I see.

September 13, 2016 at 3:10 pm

The best way to really do validation is using a GUI interface where you can gray out the Submit button until everything has been satisfied.

If you want to do an email, as you and Anthony have indicated everything stops at Read-Host. So, prior to read host, you would need to start another Powershell process with the timer running. You would need to pass the process ID of the 'waiting' script to another script that would run a loop while that process is running. Some basic pseudo-code:

New-User.ps1

Invoke-Expression -Command "C:\Users\Rob\Desktop\Start-Timer.ps1 -Process $([System.Diagnostics.Process]::GetCurrentProcess())" 
$AccountExpiry = Read-Host "Has an end date been specified on the attached Form?"

Start-Timer.ps1

param(
    $Process
)


$psScript = Get-Process | Where{$_.ID -eq $process}

while ([boolean]$psScript) {
    
    "Starting process loop..."
    
     Start-Sleep -Seconds 180
     $psScript = Get-Process | Where{$_.ID -eq $process}
    
     "Send-MailMessage"
}

"Exiting loop"

You could dynamically generate the script in the temp directory or reference a script in the script directory. It's typically a long painful path trying to build solutions around Read-Host.

  • This reply was modified 2 years ago by  Rob Simmers.

September 13, 2016 at 3:45 pm

$timer = new-object timers.timer

$action = {
	
	[TimeSpan]$span = $StartTime - (Get-Date)
	$host.ui.rawui.windowtitle = "{0:mm}:{0:ss}" -f $span
	
}

$timer.Interval = 100

$Timer.Enabled = $True

Register-ObjectEvent -InputObject $timer -EventName elapsed `
					 –SourceIdentifier Timer3 -Action $action | Out-Null

$StartTime = (Get-Date).AddMinutes(10)

$Timer.Start()


function notreadhost {
	
	write-host 'Hello..waiting on you...Are you still there (Y)?'
	
	while (-not $host.ui.RawUI.KeyAvailable) { }
	
	[string]$pressanykey = ($host.ui.RawUI.ReadKey("IncludeKeyDown,NoEcho")).character
	
	$pressanykey
	
}


notreadhost

September 13, 2016 at 4:07 pm

sleep for one second before calling the function giving the timer the ability to process the first tick.

Now all you have to do is detect the span total minutes to meet your conditions and execute your email part. Less than 3 minutes reset the starttime.

September 13, 2016 at 4:12 pm

Here is how I do this in forms so I don't have to chase down the user who left it open. The timer is a fun little toy=D


$formmousehover = {
	
	$formO365UserProvision.Text = ''
	$script:StartTime = (Get-Date).AddMinutes(10)
	[TimeSpan]$span = $script:StartTime - (Get-Date)
	$statusbar1.Text = "Form timeout: " + "{0:N0}" -f $span.TotalMinutes + " Minute(s)"
	$picturebox1.remove_MouseHover($formmousehover)
}

$timer1_Tick = {
	
	[TimeSpan]$span = $StartTime - (Get-Date)
	
	if ($span.TotalSeconds -gt 30) { $statusbar1.Text = "Form timeout: " + "{0:mm}:{0:ss}" -f $span } else {
		
		$statusbar1.Text = "Hover mouse over banner to add time."
		$formO365UserProvision.Text = "Are you still using this form? " + "{0:N0}" -f $span.TotalSeconds + " Seconds"
		
		$picturebox1.add_MouseHover($formmousehover)
		
		if ($span.TotalSeconds -le 0) {
			
			$formO365UserProvision.Close()
			
		}
		
		
	}
	
	
}

September 13, 2016 at 11:33 pm

Hi All,

Thanks for the replies. I ended up putting something together yesterday. Its pretty messy but its working. I created a small Vb.Net EXE which contains a timer that sends an email to the user running the program every 3 minutes via basic SMTP. In relation to PowerShell the EXE gets called just before the Read-Host occurs and runs in the background (no GUI) and when the Read-Host event completes the process then gets killed.

September 14, 2016 at 11:53 am

ducttapeandbandaids.ps1?

I gave you near complete code to do it. It's formatted not to be messy. Was there something you don't understand about the timer?

September 14, 2016 at 11:59 am

Rube Goldberg, you could use your webcam to watch the clock on your wall and make a sound every three minutes and train a parrot to invoke your email script.

September 14, 2016 at 1:04 pm

It appears you think i have insulted you Dan Potter. When i mentioned the term "Messy" i was referring to my own VB.Net EXE i put together (through trial and error) which had nothing to do with the code you gave me as i hadn't seen your reply until afterwards. I put this together before you even replied to this post.

Anyways i appreciate your assistance with this and apologise if you thought i was having a dig at you.

Shaun

September 14, 2016 at 1:06 pm

Not offended..just a stickler for doing things the right way:-) I work in government, you should see the solutions I've had to repair over the last two years.

September 14, 2016 at 1:21 pm

No worries, i work for the Government myself and i agree a lot of the solutions in place are a mess. I work in the account provisioning team so have had to pretty much put together all the scripts from scratch to automate the whole AP processes. Everything is pretty much in order now though and is working. If i have any further issues i'm sure you will see me back here 😉

Cheers