functions/Watch-DbaUpdate.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
Function Watch-DbaUpdate
{
<#
.SYNOPSIS
Just for fun - checks the PowerShell Gallery ever few hours for updates to dbatools - notifies max every 6 hours.
 
.DESCRIPTION
Only supports Windows 10. Not sure how to make the notification last longer (like Slack does).
  
Anyone know how to make it clickable so that it opens an URL?
 
.NOTES
Tags: JustForFun
dbatools PowerShell module (https://dbatools.io, clemaire@gmail.com)
Copyright (C) 2016 Chrissy LeMaire
 
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
 
.LINK
https://dbatools.io/Watch-DbaUpdate
 
.EXAMPLE
Watch-DbaUpdate
 
Watches the gallery for updates to dbatools.
#>
    
    BEGIN
    {
        function Show-Notification (
            $title = "dbatools update",
            $text = "Version $galleryversion is now available"
        )
        {
            $null = [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime]
            $templatetype = [Windows.UI.Notifications.ToastTemplateType]::ToastImageAndText02
            $template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($templatetype)
            
            #Convert to .NET type for XML manipuration
            $toastXml = [xml]$template.GetXml()
            $null = $toastXml.GetElementsByTagName("text").AppendChild($toastXml.CreateTextNode($title))
            
            $image = $toastXml.GetElementsByTagName("image")
            # unsure why $PSScriptRoot isnt't working here
            $base = $module.ModuleBase
            
            $image.setAttribute("src", "$base\bin\thor.png")
            $image.setAttribute("alt", "thor")
            
            #Convert back to WinRT type
            $xml = New-Object Windows.Data.Xml.Dom.XmlDocument
            $xml.LoadXml($toastXml.OuterXml)
            
            $toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
            $toast.Tag = "PowerShell"
            $toast.Group = "PowerShell"
            # This doesn't work
            $toast.ExpirationTime = [DateTimeOffset]::Now.AddHours(6)
            
            $notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($text)
            $notifier.Show($toast)
        }
        
        function Create-Task
        {
            $script = {
                try
                {
                    # create a task, check every 3 hours
                    $action = New-ScheduledTaskAction –Execute 'powershell.exe' -Argument '–NoProfile -NoLogo -NonInteractive -WindowStyle Hidden Watch-DbaUpdate'
                    $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date -RepetitionInterval (New-TimeSpan -Hours 3)
                    $principal = New-ScheduledTaskPrincipal -LogonType S4U -UserId (whoami)
                    $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([timespan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable -DontStopOnIdleEnd
                    $task = Register-ScheduledTask -Principal $principal -TaskName 'dbatools version check' -Action $action -Trigger $trigger -Settings $settings -ErrorAction Stop
                    return $true    
                }
                catch
                {
                    return $false
                }
            }
            
            # Needs admin creds to setup the kind of PowerShell window that doesn't appear for a millisecond
            # which is a millisecond too long
            If (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
            {
                return (Start-Process powershell -Verb runAs -ArgumentList $script.tostring() -ErrorAction Stop)
            }
            else
            {
                return (Invoke-Command -ScriptBlock $script -ErrorAction Stop)
            }
        }
    }
    
    PROCESS
    {
        if (([Environment]::OSVersion).Version.Major -lt 10)
        {
            Write-Warning "This command only supports Windows 10 and above"
            return
        }
        
        if ($null -eq (Get-ScheduledTask -TaskName "dbatools version check" -ErrorAction SilentlyContinue))
        {
            Write-Warning "Watch-DbaUpdate runs as a Scheduled Task which must be created. This will only happen once."
            
            $result = Create-Task
            
            if ($result -eq $false)
            {
                Write-Warning "Couldn't create task :("
                return
            }
            else
            {
                $module = Get-Module -Name dbatools
                Write-Warning "Task created! A notication should appear momentarily. Here's something cute to look at in the interim."
                Show-Notification -title "dbatools ❤ you" -text "come hang out at dbatools.io/slack"
                return
            }
        }
        
        # leave this in for the scheduled task
        $module = Get-Module -Name dbatools
        
        if (!$module)
        {
            Import-Module dbatools
            $module = Get-Module -Name dbatools
        }
        
        $galleryversion = (Find-Module -Name dbatools -Repository PSGallery).Version
        $localversion = $module.Version
        
        if ($galleryversion -le $localversion) { return }
        
        $file = "$env:LOCALAPPDATA\dbatools\watchupdate.xml"
        
        $new = [pscustomobject]@{
            NotifyTime = (Get-Date)
            NotifyVersion = $galleryversion
        }
        
        if (Test-Path $file)
        {
            $old = Import-Clixml -Path $file -ErrorAction SilentlyContinue
            
            if ($old.NotifyTime -lt (Get-Date).AddHours(-6))
            {
                Export-Clixml -InputObject $new -Path $file
                Show-Notification
            }
        }
        else
        {
            $directory = Split-Path $file
            
            if (!(Test-Path $directory))
            {
                $null = New-Item -ItemType Directory -Path $directory
            }
            
            Export-Clixml -InputObject $new -Path $file
            Show-Notification
        }
    }
}