myTickleFunctions.ps1
#requires -version 5.0 #region Define module functions Function Initialize-TickleDatabase { [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = 'default')] Param( [Parameter(Position = 0,Mandatory,HelpMessage = "Enter the folder path for the database file. If specifying a remote server the path is relative to the server." )] [ValidateScript({Test-Path $_})] [string]$DatabasePath, #Enter the name of the SQL Server instance [string]$ServerInstance = $TickleServerInstance, [Parameter(Mandatory, ParameterSetName = 'credential')] [pscredential]$Credential ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" #Enter the filename for the database file using the module variable $DatabaseName = $TickleDB $dbpath = Join-Path -Path $DatabasePath -ChildPath "$Databasename.mdf" $newDB = @" CREATE DATABASE $DatabaseName ON PRIMARY (FILENAME = '$dbpath', NAME = TickleEvents, SIZE = 10mb, MAXSIZE = 100, FILEGROWTH = 20 ) "@ $newTable = @" SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON CREATE TABLE [$databasename].[dbo].[EventData]( [EventID] [int] IDENTITY(100,1) NOT NULL, [EventDate] [datetime2](7) NOT NULL, [EventName] [nvarchar](50) NOT NULL, [EventComment] [nvarchar](50) NULL, [Archived] [bit] NULL ) ON [PRIMARY] ALTER TABLE [$databasename].[dbo].[EventData] ADD CONSTRAINT [DF_EventData_Archived] DEFAULT (N'0') FOR [Archived] "@ } #begin Process { if (Test-Path -path $dbpath) { Write-Warning "A file was already found at $dbpath. Initialization aborted." #bail out if the database file already exists return } Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating Database file $dbpath" Write-Verbose $newDB Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Connect to $ServerInstance" if ($PSBoundParameters.ContainsKey('Databasepath')) { $PSBoundParameters.Remove('Databasepath') | Out-Null } #need to connect to a database $PSBoundParameters.add("Database", 'Master') $PSBoundParameters.Add("Query", $newDB) Write-Verbose ($PSBoundParameters | Out-String) if ($PSCmdlet.ShouldProcess($dbpath)) { #create the database Try { _InvokeSqlQuery @PSBoundParameters | Out-Null } Catch { Throw $_ } #give SQL a chance to comnplete the action Start-Sleep -Seconds 2 #create the table Try { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating table EventData" Write-Verbose $newTable $PSBoundParameters.Query = $newTable $PSBoundParameters.Database = $DatabaseName Write-Verbose ($PSBoundParameters | Out-String) _InvokeSqlQuery @PSBoundParameters } Catch { Throw $_ } Write-Host "Database inialization complete." -ForegroundColor Green } #if should process } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Initialize-TickleDatabase Function Add-TickleEvent { [cmdletbinding(SupportsShouldProcess)] [OutputType("None","MyTickle")] [Alias("ate")] Param( [Parameter(Position = 0, ValueFromPipelineByPropertyName, Mandatory, HelpMessage = "Enter the name of the event")] [Alias("Name")] [string]$Event, [Parameter(Position = 1, ValueFromPipelineByPropertyName, Mandatory, HelpMessage = "Enter the datetime for the event")] [ValidateScript( { If ($_ -gt (Get-Date)) { $True } else { Throw "You must enter a future date and time." } })] [datetime]$Date, [Parameter(Position = 2, ValueFromPipelineByPropertyName)] [string]$Comment, #Enter the name of the SQL Server instance [ValidateNotNullOrEmpty()] [string]$ServerInstance = $TickleServerInstance, [pscredential]$Credential, [switch]$Passthru ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" $invokeParams = @{ Query = $null ServerInstance = $ServerInstance Database = $TickleDB ErrorAction = 'Stop' } if ($PSBoundParameters.ContainsKey('credential')) { $invokeParams.Add("credential", $Credential) } } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding event '$event'" #events with apostrophes will have them stripped off $theEvent = $Event.replace("'", '') $InvokeParams.query = "INSERT INTO EventData (EventDate,EventName,EventComment) VALUES ('$Date','$theEvent','$Comment')" $short = "[$Date] $Event" if ($PSCmdlet.ShouldProcess($short)) { Try { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] $($invokeparams.query)" _InvokeSqlQuery @invokeParams | Out-Null } Catch { throw $_ } if ($passthru) { $query = "Select Top 1 * from EventData Order by EventID Desc" $invokeParams.query = $query _InvokeSqlQuery @invokeParams | _NewMyTickle } #if passthru } #if should process } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Add-TickleEvent Function Get-TickleEvent { [cmdletbinding(DefaultParameterSetname = "Days")] [OutputType("MyTickle")] [Alias("gte")] Param( [Parameter(ParameterSetName = "ID")] [int[]]$Id, [Parameter(ParameterSetName = "Name")] [Alias("event")] [string]$Name, [Parameter(ParameterSetName = "All")] [switch]$All, [Parameter(ParameterSetName = "Expired")] [switch]$Expired, [Parameter(ParameterSetName = "Archived")] [switch]$Archived, [ValidateScript( {$_ -gt 0})] [Parameter(ParameterSetName = "Days")] [Parameter(ParameterSetName = "Offline")] [Alias("days")] [int]$Next = $TickleDefaultDays, [Parameter(ParameterSetName = "ID")] [Parameter(ParameterSetName = "Archived")] [Parameter(ParameterSetName = "Expired")] [Parameter(ParameterSetName = "All")] [Parameter(ParameterSetName = "Days")] [Parameter(ParameterSetName = "Name")] #Enter the name of the SQL Server instance [ValidateNotNullOrEmpty()] [string]$ServerInstance = $TickleServerInstance, [Parameter(ParameterSetName = "ID")] [Parameter(ParameterSetName = "Archived")] [Parameter(ParameterSetName = "Expired")] [Parameter(ParameterSetName = "All")] [Parameter(ParameterSetName = "Days")] [Parameter(ParameterSetName = "Name")] [pscredential]$Credential, [Parameter(ParameterSetName = "Offline")] #Enter the path to an offline CSV file [ValidatePattern('\.csv$')] [ValidateScript( {Test-Path $_})] [string]$Offline ) Write-Verbose "[$((Get-Date).TimeofDay)] Starting $($myinvocation.mycommand)" $invokeParams = @{ Query = $null Database = $TickleDB ServerInstance = $ServerInstance ErrorAction = "Stop" } if ($PSBoundParameters.ContainsKey('credential')) { $invokeParams.Add("credential", $Credential) } Switch ($pscmdlet.ParameterSetName) { "ID" { Write-Verbose "[$((Get-Date).TimeofDay)] by ID" $filter = "Select * from EventData where EventID='$ID'" } "Name" { Write-Verbose "[$((Get-Date).TimeofDay)] by Name" #get events that haven't expired or been archived by name if ($name -match "\*") { $name = $name.replace("*","%") } $filter = "Select * from EventData where EventName LIKE '$Name' AND Archived='False' AND EventDate>'$(Get-Date)'" } "Days" { Write-Verbose "[$((Get-Date).TimeofDay)] for the next $next days" $target = (Get-Date).Date.AddDays($next).toString() $filter = "Select * from EventData where Archived='False' AND EventDate<='$target' AND eventdate > '$((Get-date).ToString())' ORDER by EventDate Asc" } "Expired" { Write-Verbose "[$((Get-Date).TimeofDay)] by Expiration" #get expired events that have not been archived $filter = "Select * from EventData where Archived='False' AND EventDate<'$(Get-Date)' ORDER by EventDate Asc" } "Archived" { Write-Verbose "[$((Get-Date).TimeofDay)] by Archive" $filter = "Select * from EventData where Archived='True' ORDER by EventDate Asc" } "All" { Write-Verbose "[$((Get-Date).TimeofDay)] All" #get all non archived events $filter = "Select * from EventData where Archived='False' ORDER by EventDate Asc" } "Offline" { Write-Verbose "[$((Get-Date).TimeofDay)] Offline" Write-Verbose "[$((Get-Date).TimeOfDay)] Getting offline data from $Offline" #skip any expired entries when working offline $data = import-csv -Path $Offline | where-object {[datetime]$_.Date -ge (Get-Date).Date} | _NewMyTickle } Default { #this should never get called Write-Verbose "[$((Get-Date).TimeofDay)] Default" #get events that haven't been archived $filter = "Select * from EventData where Archived='False' AND EventDate>='$(Get-Date)' ORDER by EventDate Asc" } } #switch #if using offline data, display the results if ($Offline -AND $data) { Write-Verbose "[$((Get-Date).TimeofDay)] Getting events for the next $Next days." $Data | Where-Object {$_.Date -le (Get-Date).Date.addDays($Next) } } else { Write-Verbose "[$((Get-Date).TimeofDay)] Importing events from $TickleDB on $ServerInstance" #Query database for matching events Write-Verbose "[$((Get-Date).TimeofDay)] $filter" $invokeParams.query = $filter Try { $events = _InvokeSqlQuery @invokeParams # Invoke-SqlCmd @invokeParams #convert the data into mytickle objects $data = $events | _NewMyTickle } Catch { Throw $_ } Write-Verbose "[$((Get-Date).TimeofDay)] Found $($events.count) matching events" #write event data to the pipeline $data } #else query for data Write-Verbose "[$((Get-Date).TimeofDay)] Ending $($myinvocation.mycommand)" } #Get-TickleEvent Function Set-TickleEvent { [cmdletbinding(SupportsShouldProcess, DefaultParameterSetname = "column")] [OutputType("None","MyTickle")] [Alias("ste")] Param( [Parameter(Position = 0, ValueFromPipelineByPropertyName, Mandatory)] [int32]$ID, [Parameter(ParameterSetName = "column")] [alias("Name")] [string]$Event, [Parameter(ParameterSetName = "column")] [datetime]$Date, [Parameter(ParameterSetName = "column")] [string]$Comment, #Enter the name of the SQL Server instance [ValidateNotNullOrEmpty()] [string]$ServerInstance = $TickleServerInstance, [pscredential]$Credential, [switch]$Passthru, [Parameter(ParameterSetName = "archive")] [switch]$Archive ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" $update = @" UPDATE EventData SET {0} Where EventID='{1}' "@ $invokeParams = @{ Query = $null Database = $TickleDB ServerInstance = $ServerInstance ErrorAction = "Stop" } if ($PSBoundParameters.ContainsKey('credential')) { $invokeParams.Add("credential", $Credential) } } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Updating Event ID $ID " $cols = @() if ($pscmdlet.ParameterSetName -eq 'column') { if ($Event) { $cols += "EventName='$Event'" } if ($Comment) { $cols += "EventComment='$Comment'" } if ($Date) { $cols += "EventDate='$Date'" } } else { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Archiving" $cols += "Archived='True'" } $data = $cols -join "," $query = $update -f $data, $ID $invokeParams.query = $query if ($PSCmdlet.ShouldProcess($query)) { _InvokeSqlQuery @invokeParams | Out-Null if ($Passthru) { Get-TickleEvent -id $ID } } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Update-MyTickleEvent Function Remove-TickleEvent { [cmdletbinding(SupportsShouldProcess)] [OutputType("None")] [Alias("rte")] Param( [Parameter(Position = 0, Mandatory, ValueFromPipelineByPropertyName)] [int32]$ID, #Enter the name of the SQL Server instance [ValidateNotNullOrEmpty()] [string]$ServerInstance = $TickleServerInstance, [pscredential]$Credential ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" $invokeParams = @{ Query = $null ServerInstance = $ServerInstance Database = $tickleDB ErrorAction = "Stop" } if ($PSBoundParameters.ContainsKey('credential')) { $invokeParams.Add("credential", $Credential) } } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Deleting tickle event $ID " $invokeParams.query = "DELETE From EventData where EventID='$ID'" if ($PSCmdlet.ShouldProcess("Event ID $ID")) { Try { _InvokeSqlQuery @invokeParams | Out-Null } Catch { Throw $_ } } #should process } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Remove-TickleEvent Function Export-TickleDatabase { [cmdletbinding()] [OutputType("None")] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "The path and filename for the export xml file.")] [String]$Path, #Enter the name of the SQL Server instance [ValidateNotNullOrEmpty()] [string]$ServerInstance = $TickleServerInstance, [pscredential]$Credential ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" $invokeParams = @{ Query = "Select * from $tickleTable" ServerInstance = $ServerInstance Database = $tickleDB ErrorAction = "Stop" } if ($PSBoundParameters.ContainsKey('credential')) { $invokeParams.Add("credential", $Credential) } } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Exporting database to $Path " Try { _InvokeSqlQuery @invokeParams | Export-clixml -Path $Path } Catch { throw $_ } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Export-TickleEventDatabase Function Import-TickleDatabase { [cmdletbinding(SupportsShouldProcess)] [OutputType("None")] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "The path and filename for the export xml file.")] [ValidateScript( {Test-Path $_})] [String]$Path, #Enter the name of the SQL Server instance [ValidateNotNullOrEmpty()] [string]$ServerInstance = $TickleServerInstance, [pscredential]$Credential ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" $invokeParams = @{ Query = "" ServerInstance = $ServerInstance Database = $tickleDB ErrorAction = "Stop" } if ($PSBoundParameters.ContainsKey('credential')) { $invokeParams.Add("credential", $Credential) } #turn off identity_insert $invokeParams.query = "Set identity_insert EventData On" _InvokeSqlQuery @invokeParams | out-null } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Importing database data from $Path " Try { Import-clixml -Path $path | foreach-object { $query = @" Set identity_insert EventData On INSERT INTO EventData (EventID,EventDate,EventName,EventComment,Archived) VALUES ('$($_.EventID)','$($_.EventDate)','$(($_.EventName).replace("'",""))','$($_.EventComment)','$($_.Archived)') Set identity_insert EventData Off "@ $invokeparams.query = $query Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] $($invokeparams.query)" if ($pscmdlet.ShouldProcess("VALUES ('$($_.EventID)','$($_.EventDate)','$($_.EventName)','$($_.EventComment)','$($_.Archived)'")) { _InvokeSqlQuery @invokeParams | Out-Null } } } Catch { throw $_ } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Import-TickleEventDatabase Function Show-TickleEvent { [cmdletbinding(DefaultParameterSetName = "instance")] [OutputType("None")] [Alias("shte")] Param( [ValidateScript( {$_ -ge 1})] #the next number of days to get [int]$Days = $TickleDefaultDays, [Parameter(ParameterSetName = "instance")] #Enter the name of the SQL Server instance [ValidateNotNullOrEmpty()] [string]$ServerInstance = $TickleServerInstance, [Parameter(ParameterSetName = "instance")] [pscredential]$Credential, [Parameter(ParameterSetName = "offline")] #Enter the path to an offline CSV file [ValidatePattern('\.csv$')] [ValidateScript( {Test-Path $_})] [string]$Offline ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" if ($PSCmdlet.ParameterSetName -eq 'instance') { $invokeParams = @{ Days = $Days ServerInstance = $ServerInstance } if ($PSBoundParameters.ContainsKey('credential')) { $invokeParams.Add("credential", $Credential) } } else { $invokeParams = @{Offline = $Offline} } } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting events for the next $Days days." if ($offline) { $target = (Get-Date).Date.AddDays($Days) $upcoming = Get-TickleEvent @invokeParams | Where-Object {$_.Date -le $Target} } else { Try { $upcoming = Get-TickleEvent @invokeParams } Catch { Throw $_ } } if ($upcoming) { #how wide should the box be? #get the length of the longest line $l = 0 foreach ($item in $upcoming) { #turn countdown into a string without the milliseconds $count = $item.countdown.ToString() $time = $count.Substring(0, $count.lastindexof(".")) #add the time as a new property $item | Add-Member -MemberType Noteproperty -name Time -Value $time $a = "$($item.event) $($item.Date) [$time]".length if ($a -gt $l) {$l = $a} $b = $item.comment.Length if ($b -gt $l) {$l = $b} } [int]$width = $l + 5 $header = "* Reminders $((Get-Date).ToShortDateString()) " #display events Write-Host "`r" Write-host "$($header.padright($width,"*"))" -ForegroundColor Cyan Write-Host "*$(' '*($width-2))*" -ForegroundColor Cyan foreach ($event in $upcoming) { if ($event.countdown.totalhours -le 24) { $color = "Red" } elseif ($event.countdown.totalhours -le 48) { $color = "Yellow" } else { $color = "Green" } #define the message string $line1 = "* $($event.event) $($event.Date) [$($event.time)]" if ($event.comment -match "\w+") { $line2 = "* $($event.Comment)" $line3 = "*" } else { $line2 = "*" $line3 = $null } $msg = @" $($line1.padRight($width-1))* $($line2.padright($width-1))* "@ if ($line3) { #if there was a comment add a third line that is blank $msg += "`n$($line3.padright($width-1))*" } Write-Host $msg -ForegroundColor $color } #foreach Write-Host ("*" * $width) -ForegroundColor Cyan Write-Host "`r" } #if upcoming events found else { $msg = @" ********************** * No event reminders * ********************** "@ Write-Host $msg -foregroundcolor Green } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Show-TickleEvent #endregion #region private functions function _NewMyTickle { [cmdletbinding()] [OutputType("MyTickle")] Param( [Parameter(ValueFromPipelineByPropertyName)] [alias("ID")] [int32]$EventID, [Parameter(ValueFromPipelineByPropertyName)] [alias("Event", "Name")] [string]$EventName, [Parameter(ValueFromPipelineByPropertyName)] [alias("Date")] [datetime]$EventDate, [Parameter(ValueFromPipelineByPropertyName)] [Alias("Comment")] [string]$EventComment ) Process { New-Object -TypeName mytickle -ArgumentList @($eventID, $Eventname, $EventDate, $EventComment) } } #close _NewMyTickle Function _InvokeSqlQuery { [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = "Default")] [OutputType([PSObject])] Param( [Parameter(Position = 0, Mandatory, HelpMessage = "The T-SQL query to execute")] [ValidateNotNullorEmpty()] [string]$Query, [Parameter(Mandatory, HelpMessage = "The name of the database")] [ValidateNotNullorEmpty()] [string]$Database, [Parameter(Mandatory, ParameterSetName = 'credential')] [pscredential]$Credential, #The server instance name [ValidateNotNullorEmpty()] [string]$ServerInstance = "$(hostname)\SqlExpress" ) Begin { Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)" if ($PSCmdlet.ParameterSetName -eq 'credential') { $username = $Credential.UserName $password = $Credential.GetNetworkCredential().Password } Write-Verbose "[BEGIN ] Creating the SQL Connection object" $connection = New-Object system.data.sqlclient.sqlconnection Write-Verbose "[BEGIN ] Creating the SQL Command object" $cmd = New-Object system.Data.SqlClient.SqlCommand } #begin Process { Write-Verbose "[PROCESS] Opening the connection to $ServerInstance" Write-Verbose "[PROCESS] Using database $Database" if ($Username -AND $password) { Write-Verbose "[PROCESS] Using credential" $connection.connectionstring = "Data Source=$ServerInstance;Initial Catalog=$Database;User ID=$Username;Password=$Password;" } else { Write-Verbose "[PROCESS] Using Windows authentication" $connection.connectionstring = "Data Source=$ServerInstance;Initial Catalog=$Database;Integrated Security=SSPI;" } Write-Verbose "[PROCESS] Opening Connection" Write-Verbose "[PROCESS] $($connection.ConnectionString)" Try { $connection.open() } Catch { Throw $_ #bail out Return } #join the connection to the command object $cmd.connection = $connection $cmd.CommandText = $query Write-Verbose "[PROCESS] Invoking $query" if ($PSCmdlet.ShouldProcess($Query)) { #determine what method to invoke based on the query Switch -regex ($query) { "^Select (\w+|\*)|(@@\w+ AS)" { Write-Verbose "ExecuteReader" $reader = $cmd.executereader() $out = @() #convert datarows to a custom object while ($reader.read()) { $h = [ordered]@{} for ($i = 0; $i -lt $reader.FieldCount; $i++) { $col = $reader.getname($i) $h.add($col, $reader.getvalue($i)) } #for $out += new-object -TypeName psobject -Property $h } #while $out $reader.close() Break } "@@" { Write-Verbose "ExecuteScalar" $cmd.ExecuteScalar() Break } Default { Write-Verbose "ExecuteNonQuery" $cmd.ExecuteNonQuery() } } } #should process } End { Write-Verbose "[END ] Closing the connection" $connection.close() Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)" } #end } #close _InvokeSqlQuery #endregion |