diff --git a/PSAppDeplyToolkit/Assets/AppIcon.png b/PSAppDeplyToolkit/Assets/AppIcon.png
new file mode 100644
index 0000000..8b5f19d
Binary files /dev/null and b/PSAppDeplyToolkit/Assets/AppIcon.png differ
diff --git a/PSAppDeplyToolkit/Assets/Banner.Classic.png b/PSAppDeplyToolkit/Assets/Banner.Classic.png
new file mode 100644
index 0000000..43b03b6
Binary files /dev/null and b/PSAppDeplyToolkit/Assets/Banner.Classic.png differ
diff --git a/PSAppDeplyToolkit/Config/config.psd1 b/PSAppDeplyToolkit/Config/config.psd1
new file mode 100644
index 0000000..4dbc681
--- /dev/null
+++ b/PSAppDeplyToolkit/Config/config.psd1
@@ -0,0 +1,402 @@
+@{
+ Assets = @{
+ # Specify filename of the logo.
+ Logo = '..\Assets\AppIcon.png'
+
+ # Specify filename of the banner (Classic-only).
+ Banner = '..\Assets\Banner.Classic.png'
+ }
+
+ MSI = @{
+ # Installation parameters used for non-silent MSI actions.
+ InstallParams = 'REBOOT=ReallySuppress /QB-!'
+
+ # Logging level used for MSI logging.
+ LoggingOptions = '/L*V'
+
+ # Log path used for MSI logging.
+ LogPath = '$envWinDir\Logs\Software'
+
+ # Log path used for MSI logging when RequireAdmin is False.
+ LogPathNoAdminRights = '$envProgramData\Logs\Software'
+
+ # The length of time in seconds to wait for the MSI installer service to become available. Default is 600 seconds (10 minutes).
+ MutexWaitTime = 600
+
+ # Installation parameters used for silent MSI actions.
+ SilentParams = 'REBOOT=ReallySuppress /QN'
+
+ # Installation parameters used for MSI uninstall actions.
+ UninstallParams = 'REBOOT=ReallySuppress /QN'
+ }
+
+ Toolkit = @{
+ # Specify the path for the cache folder.
+ CachePath = '$envProgramData\SoftwareCache'
+
+ # Specify if the log files should be bundled together in a compressed zip file.
+ CompressLogs = $false
+
+ # Choose from either 'Native' for native PowerShell file copy via Copy-ADTItem, or 'Robocopy' to use robocopy.exe.
+ FileCopyMode = 'Native'
+
+ # Specify if an existing log file should be appended to.
+ LogAppend = $true
+
+ # Specify if debug messages such as bound parameters passed to a function should be logged.
+ LogDebugMessage = $false
+
+ # Specify maximum number of previous log files to retain.
+ LogMaxHistory = 10
+
+ # Specify maximum file size limit for log file in megabytes (MB).
+ LogMaxSize = 10
+
+ # Log path used for Toolkit logging.
+ LogPath = '$envWinDir\Logs\Software'
+
+ # Same as LogPath but used when RequireAdmin is False.
+ LogPathNoAdminRights = '$envProgramData\Logs\Software'
+
+ # Specifies that a subfolder based on InstallName should be used for all log capturing.
+ LogToSubfolder = $false
+
+ # Specify if log file should be a CMTrace compatible log file or a Legacy text log file.
+ LogStyle = 'CMTrace'
+
+ # Specify if log messages should be written to the console.
+ LogWriteToHost = $true
+
+ # Specify if console log messages should bypass PowerShell's subsystems and be sent direct to stdout/stderr.
+ # This only applies if "LogWriteToHost" is true, and the script is being ran in a ConsoleHost (not the ISE, or another host).
+ LogHostOutputToStdStreams = $false
+
+ # Automatically changes DeployMode to Silent during the OOBE.
+ OobeDetection = $true
+
+ # Registry key used to store toolkit information (with PSAppDeployToolkit as child registry key), e.g. deferral history.
+ RegPath = 'HKLM:\SOFTWARE'
+
+ # Same as RegPath but used when RequireAdmin is False. Bear in mind that since this Registry Key should be writable without admin permission, regular users can modify it also.
+ RegPathNoAdminRights = 'HKCU:\SOFTWARE'
+
+ # Specify if Administrator Rights are required. Note: Some functions won't work if this is set to false, such as deferral, block execution, file & registry RW access and potentially logging.
+ RequireAdmin = $true
+
+ # Automatically changes DeployMode for session zero (SYSTEM) operations.
+ SessionDetection = $true
+
+ # Path used to store temporary Toolkit files (with PSAppDeployToolkit as subdirectory), e.g. cache toolkit for cleaning up blocked apps. Normally you don't want this set to a path that is writable by regular users, this might lead to a security vulnerability. The default Temp variable for the LocalSystem account is C:\Windows\Temp.
+ TempPath = '$envTemp'
+
+ # Same as TempPath but used when RequireAdmin is False.
+ TempPathNoAdminRights = '$envTemp'
+ }
+
+ UI = @{
+ # Used to turn automatic balloon notifications on or off.
+ BalloonNotifications = $true
+
+ # The name to show by default for all balloon notifications.
+ BalloonTitle = 'PSAppDeployToolkit'
+
+ # Choose from either 'Fluent' for contemporary dialogs, or 'Classic' for PSAppDeployToolkit 3.x WinForms dialogs.
+ DialogStyle = 'Fluent'
+
+ # Exit code used when a UI prompt times out.
+ DefaultExitCode = 1618
+
+ # Time in seconds after which the prompt should be repositioned centre screen when the -PersistPrompt parameter is used. Default is 60 seconds.
+ DefaultPromptPersistInterval = 60
+
+ # Time in seconds to automatically timeout installation dialogs. Default is 55 minutes so that dialogs timeout before Intune times out.
+ DefaultTimeout = 3300
+
+ # Exit code used when a user opts to defer.
+ DeferExitCode = 60012
+
+ # Specify whether to re-enumerate running processes dynamically while displaying Show-ADTInstallationWelcome.
+ # If the CloseProcesses items were not running when the prompt was displayed, and are subsequently detected to be running, the prompt will be updated with the apps to close.
+ # If the CloseProcesses items were running when the prompt was displayed and are subsequently detected not to be running then the installation will automatically continue if deferral is not available.
+ # If the running applications change (new CloseProcesses launched or running processes closed), the list box will dynamically update to reflect the currently running applications.
+ DynamicProcessEvaluation = $true
+
+ # Time in seconds after which to re-enumerate running processes while displaying the Show-ADTInstallationWelcome prompt. Default is 2 seconds.
+ DynamicProcessEvaluationInterval = 2
+
+ <# Specify a static UI language using the one of the Language Codes listed below to override the language culture detected on the system.
+ Language Code Language | Language Code Language
+ ============= ======== | ============= ========
+ AR Arabic | KO Korean
+ CZ Czech | NL Dutch
+ DA Danish | NB Norwegian (Bokmål)
+ DE German | PL Polish
+ EN English | PT Portuguese (Portugal)
+ EL Greek | PT-BR Portuguese (Brazil)
+ ES Spanish | RU Russian
+ FI Finnish | SK Slovak
+ FR French | SV Swedish
+ HE Hebrew | TR Turkish
+ HU Hungarian | ZH-Hans Chinese (Simplified)
+ IT Italian | ZH-Hant Chinese (Traditional)
+ JA Japanese |
+ #>
+ LanguageOverride = $null
+
+ # Time in seconds after which to re-prompt the user to close applications in case they ignore the prompt or they cancel the application's save prompt.
+ PromptToSaveTimeout = 120
+
+ # Time in seconds after which the restart prompt should be re-displayed/repositioned when the -NoCountdown parameter is specified. Default is 600 seconds.
+ RestartPromptPersistInterval = 600
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDwDj8+4CuYCZXD
+# Te8j83I6+zljIMQv4xnE1sDvMTx+g6CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgQ1a7AJE6ncffXOVZpTXpD3O5oZxa0Nn/XEal9rVxKBAw
+# DQYJKoZIhvcNAQEBBQAEggGAKCjfE8EVh9BHUJqh8GTaL1s2kBC9dZKyNyMwUdM0
+# qwXjhSeqmIXzaW4Atz2KtCoyYG5g3xMPQkKVZ0hBF7iY2QKnOZVNEEavKJAr/RuX
+# kIFmxOOD4Zzvl5at8TKhlD3VJE1BngYO0qJTezT/vD1Htjvi4fm0ZAf5n4NDCZsF
+# OZJZNKCWCHIPlOEP2SUiW2mE/ttrxV1EArujNOaptJKqbyWXfcSPvObBXPsm6x8f
+# VTvb9/jz69q9IzM7O8QeKFnsajfws9DcUec5FrqOhCRZXOiI+OiguOtgpY348QfV
+# zNvKT26n7gkPZdztKRev1j3g4o2OYq4xQzdCam7r9+XITRXFgETRaU9Mx0lZ/fVz
+# OMRUZF7nAPuVOxdnDTo9C6XoFqycikdkz1Vyu60OUmiBNTUT9URL/DYbWq0ITYFH
+# YVp1Y7c3e3JZhRMwv24gwcIgRf56HZACcxHAS/onYckTb6OkFYLMJChBqD9PS39X
+# eTWlurdMeltw3AFuN2CX57s6oYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIPoTA8/d
+# T0daKmUs8BX7u5dKR9tchSaxS08fZbs2ny13AhEA9j5lB29EEMc3fV1WIDmofxgP
+# MjAyNTAyMjMyMzQ2NDlaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ2NDlaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCBDqun3l+eL9F2mNo+G70uiVHyDBlBgr6ydshVZ
+# MeX32jA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgBqY432+7l30W2SaNZ1VnaO
+# X0OYZdX3pm//8IQcAkcVmvindvMtyjO77pPuhPsXVKV8oMLABc/sXWmHYj7LPbWA
+# AKBCx4qp2To5RIzFJ/p8Bgn8PY7KWJdXefZAPkJvIOOUObhSISvItLFbg1naVN4n
+# zEdlwftHcsdl8uACRzpFGmPz3/6SXNIbt/u6dwJdo2fa80xSD9h4KWAb2sRYAcRJ
+# wf4jpJP1/zxadHgkNLqHVchp0LE1udWRMcMUtnAzHicK17vJAvStdEUi/Jlt8Uyj
+# PHAAckF7wVxOh88YEqfw18TbuP3StsVp96QBVK9Pzgg5d9OFgxcnZBg+JiIT4jsB
+# LhrpvBeSfV4cMBqZ0GNUqc2eOKfEdEnzOFSQ9rtcfpAD4mn0owQ77pSStkzFGTnp
+# iWa1OaQzSKgA4tLqiqhw9W7X5u2mJAC16jsvsYGJEihS4ACUIAZsBWkEbuLb11GB
+# So6tXJnWwSpzTUQiqt9D6K9/qOBBrGYAF7oCAyxHOUoluSBBbzkx4iRU74piZfLV
+# nWXdK4bN1MK9XMvFwWzkg6JKQ49IqeIDzSCpBHPzeSPP4jGNkDERlalQacFqb7ry
+# FyAUVU2KscEsrnVF8kwOn2NoTmU/dsF7/J1JHklKGxc/n+jcl2P99vD5gQHKncUc
+# j0SivFbo6y1EJLSFhGSR7g==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/AppDeployToolkitExtensions.ps1 b/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/AppDeployToolkitExtensions.ps1
new file mode 100644
index 0000000..8961088
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/AppDeployToolkitExtensions.ps1
@@ -0,0 +1,300 @@
+<#
+
+.SYNOPSIS
+PSAppDeployToolkit - Provides the ability to extend and customize the toolkit by adding your own functions that can be re-used.
+
+.DESCRIPTION
+This script is a template that allows you to extend the toolkit with your own custom functions.
+
+This script is dot-sourced by the AppDeployToolkitMain.ps1 script which contains the logic and functions required to install or uninstall an application.
+
+PSAppDeployToolkit is licensed under the GNU LGPLv3 License - (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation, either version 3 of the License, or 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 Lesser General Public License along with this program. If not, see .
+
+.INPUTS
+None. You cannot pipe objects to this script.
+
+.OUTPUTS
+None. This script does not generate any output.
+
+.LINK
+https://psappdeploytoolkit.com
+
+#>
+
+##*===============================================
+##* MARK: VARIABLE DECLARATION
+##*===============================================
+
+
+##*===============================================
+##* MARK: FUNCTION LISTINGS
+##*===============================================
+
+
+##*===============================================
+##* MARK: SCRIPT BODY
+##*===============================================
+
+if ((Test-Path -LiteralPath Microsoft.PowerShell.Core\Variable::scriptParentPath) -and $scriptParentPath)
+{
+ Write-ADTLogEntry -Message "Script [$($MyInvocation.MyCommand.Definition)] dot-source invoked by [$(((Get-Variable -Name MyInvocation).Value).ScriptName)]" -ScriptSection Initialization
+}
+else
+{
+ Write-ADTLogEntry -Message "Script [$($MyInvocation.MyCommand.Definition)] invoked directly" -ScriptSection Initialization
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC5TTZYf7/+DSiq
+# Ydk2EDdpod+1wmZkilVxU6oumZT5WaCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgZOAbNiiE6nUgEUxc13onaUFFQL8JL4IGHT1hC4KIiZAw
+# DQYJKoZIhvcNAQEBBQAEggGABMs5GmiJb6FloIK85Hp1x+2Ma9lHSAuZBKOhIO3e
+# y/7AYMNmZddiUp7ywWRO4pDVoDg3SfcAtp95EldekSILNPcmbp61GSKAtIy+Z8ab
+# 23iR0j+BVM3y5sP2P/cQAp3UdkrhJO1FmnnAV5wuoL3D+r/Za6iAdwTI0xIIhwcl
+# pB99uVwMi6n6wSHjbijSr/Rg4Oj7BKoPYHGErG66BfzJTtlX9Q0PWItjN/zco1J+
+# ENI0lqNuiDuX1aVEwhfRzlnyZZtbDFBuravsanOn/zrU8nJDyE0lArDOfxJjv4WA
+# ls33rk3MbLACMMc1q44jF5SqcPQwx9bFoPysSAxpWWa91w3VZHqAaLhFQtz9oaSA
+# gltZiUBl+8eYJYsfiss/QxDLJTRIi6r6bqoJ9canZdZ5rzeDYdKwUGnpB/6wIP4C
+# Nwbqk911cnnLI+iAyvuafPCpYOSNUpCSHPcjUg7/vp0uyU8IYSHqcl6hxu6/O9yp
+# QKzp+58lmCvxOK2qsIr8ItDDoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIF/4YGuj
+# Am0m8NF1mb8WfpJVZyTtT2P5zuVyoT/vq+SLAhEA5mN6BVkMvwzM/FL9TAba7xgP
+# MjAyNTAyMjMyMzQ2NTJaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ2NTJaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCCm1FkLfqRKZl85bW5lI2WLKomG4F1R17yfwHFU
+# VPda/zA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgBo8IhfSDxPu0ZCawzRNwmG
+# 8KZeZv2Z34z4fuxjosUD21OdyEFla4A3JSbYQ4gud4HgQM+hyoklyR3rR73+BwvX
+# c3LiZ6LrTVz5gzvnrZeRwjf6dUNiQCNjKPEMQS/DT0ZUTbj6DPqyE+cLMljPvM7B
+# h8AIcM5SNBjNiMCbRzZ8zg0XH022Sb4KlhwXOez3p8IegaazgKKWGXNnxhW5hyoj
+# FwGM9L9fxrUKrEKfDy3m4Bf6iNy0rXk/ZUYRLAhnBpT8v8otaokso4fG+MawFWu6
+# coGqE0QTDaqE/RzWdDr+eqvESpWgh6g8EE89Xgj+fCSl7Iz4sll3/8l16V08t5tO
+# wwQ5KSL2Oqa4WpiovnUvrlDGFyastZkZfTOR2M3Wed5YxbmT1JO1NHCzTp3XcROz
+# 4PISw5JpvlWrKequ8CikDf75VmwY4G0bJ/wZf56Kgn8nJRxxx4gIs4S2IWS6gxqL
+# 5sWM74zWNlMa77uq+X+p8LwvubXx+N4CakZBZGUfaRXYnUjVIhlEgn2Qepmf1fxD
+# CijBwpmUtP1jZlo4hm5OQisYgNYjJmIroQoYXzsBHb7PeeZFzkT0lRbQd69s6K+g
+# 3aFT0Nw5IqT9LaOIzi4vphr92UfnTEU9iDo8IkEpg/xEbnQwOh4v3dSlwQ4tBvei
+# NMtDm73LMQJiKXqg++FPRg==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/AppDeployToolkitMain.ps1 b/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/AppDeployToolkitMain.ps1
new file mode 100644
index 0000000..6b35601
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/AppDeployToolkitMain.ps1
@@ -0,0 +1,5401 @@
+<#
+
+.SYNOPSIS
+PSAppDeployToolkit - This script contains PSAppDeployToolkit v3.x API wrappers to provide backwards compatibility for Deploy-Application.ps1 scripts against PSAppDeployToolkit v4.
+
+.DESCRIPTION
+The script can be called directly to dot-source the toolkit functions for testing, but it is usually called by the Deploy-Application.ps1 script.
+
+The script can usually be updated to the latest version without impacting your per-application Deploy-Application scripts. Please check release notes before upgrading.
+
+PSAppDeployToolkit is licensed under the GNU LGPLv3 License - (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation, either version 3 of the License, or 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 Lesser General Public License along with this program. If not, see .
+
+.INPUTS
+None. You cannot pipe objects to this script.
+
+.OUTPUTS
+None. This script does not generate any output.
+
+.LINK
+https://psappdeploytoolkit.com
+
+#>
+
+#---------------------------------------------------------------------------
+#
+# MARK: Initialization code
+#
+#---------------------------------------------------------------------------
+
+[CmdletBinding()]
+param
+(
+)
+
+# Remove all functions defined in this script from the function provider.
+Remove-Item -LiteralPath ($adtWrapperFuncs = $MyInvocation.MyCommand.ScriptBlock.Ast.EndBlock.Statements | & { process { if ($_ -is [System.Management.Automation.Language.FunctionDefinitionAst]) { return "Microsoft.PowerShell.Core\Function::$($_.Name)" } } }) -Force -ErrorAction Ignore
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Write-ADTLogEntry
+#
+#---------------------------------------------------------------------------
+
+function Write-Log
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidOverwritingBuiltInCmdlets', '', Justification = "Apparently 'Write-Log' was a shipped cmdlet in PowerShell Core 6.1.x. We can't rename this wrapper so we must suppress.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [AllowEmptyCollection()]
+ [Alias('Text')]
+ [System.String[]]$Message,
+
+ [Parameter(Mandatory = $false, Position = 1)]
+ [ValidateRange(0, 3)]
+ [System.Int16]$Severity,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Source,
+
+ [Parameter(Mandatory = $false, Position = 3)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ScriptSection,
+
+ [Parameter(Mandatory = $false, Position = 4)]
+ [ValidateSet('CMTrace', 'Legacy')]
+ [System.String]$LogType,
+
+ [Parameter(Mandatory = $false, Position = 5)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LogFileDirectory,
+
+ [Parameter(Mandatory = $false, Position = 6)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LogFileName,
+
+ [Parameter(Mandatory = $false, Position = 7)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$AppendToLogFile,
+
+ [Parameter(Mandatory = $false, Position = 8)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$MaxLogHistory,
+
+ [Parameter(Mandatory = $false, Position = 9)]
+ [ValidateNotNullOrEmpty()]
+ [System.Decimal]$MaxLogFileSizeMB,
+
+ [Parameter(Mandatory = $false, Position = 10)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true,
+
+ [Parameter(Mandatory = $false, Position = 11)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$WriteHost,
+
+ [Parameter(Mandatory = $false, Position = 12)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false, Position = 13)]
+ [System.Management.Automation.SwitchParameter]$DebugMessage,
+
+ [Parameter(Mandatory = $false, Position = 14)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$LogDebugMessage
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Write-ADTLogEntry]. Please migrate your scripts to use the new function." -Severity 2 -Source $MyInvocation.MyCommand.Name -DebugMessage:$noDepWarnings
+
+ # Announce dead parameters.
+ $null = ('AppendToLogFile', 'MaxLogHistory', 'MaxLogFileSizeMB', 'WriteHost', 'LogDebugMessage').ForEach({
+ if ($PSBoundParameters.ContainsKey($_))
+ {
+ Write-ADTLogEntry -Message "The parameter '-$_' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $PSBoundParameters.Remove($_)
+ }
+ })
+
+ # There should never be a time where we can't log.
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ # Set up collector for piped in messages.
+ $messages = [System.Collections.Specialized.StringCollection]::new()
+ }
+
+ process
+ {
+ # Add all non-null messages to the collector.
+ $null = $Message | & {
+ process
+ {
+ if (![System.String]::IsNullOrWhiteSpace($_))
+ {
+ $messages.Add($_)
+ }
+ }
+ }
+ }
+
+ end
+ {
+ # Process provided messages if we have any.
+ if ($messages.Count)
+ {
+ try
+ {
+ $PSBoundParameters.Message = $messages
+ Write-ADTLogEntry @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Close-ADTSession
+#
+#---------------------------------------------------------------------------
+
+function Exit-Script
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$ExitCode
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Close-ADTSession]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Close-ADTSession @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Invoke-ADTAllUsersRegistryAction
+#
+#---------------------------------------------------------------------------
+
+function Invoke-HKCURegistrySettingsForAllUsers
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({ if ($_ -match '\$UserProfile\.SID') { Write-ADTLogEntry -Message "The base function [Invoke-ADTAllUsersRegistryAction] no longer supports the use of [`$UserProfile]. Please use [`$_] or [`$PSItem] instead." -Severity 2 }; ![System.String]::IsNullOrWhiteSpace($_) })]
+ [Alias('RegistrySettings')]
+ [System.Management.Automation.ScriptBlock]$ScriptBlock,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Types.UserProfile[]]$UserProfiles
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Invoke-ADTAllUsersRegistryAction]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $PSBoundParameters.ScriptBlock = { New-Variable -Name UserProfile -Value $_ -Force }, $PSBoundParameters.ScriptBlock
+ try
+ {
+ Invoke-ADTAllUsersRegistryAction @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Replacement for Get-HardwarePlatform
+#
+#---------------------------------------------------------------------------
+
+function Get-HardwarePlatform
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [`$envHardwareType]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ return $envHardwareType
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTFreeDiskSpace
+#
+#---------------------------------------------------------------------------
+
+function Get-FreeDiskSpace
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Drive,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTFreeDiskSpace]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ try
+ {
+ Get-ADTFreeDiskSpace @PSBoundParameters
+ }
+ catch
+ {
+ Write-ADTLogEntry -Message "Failed to retrieve free disk space for drive [$Drive].`n$(Resolve-ADTErrorRecord -ErrorRecord $_)" -Severity 3
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Remove-ADTInvalidFileNameChars
+#
+#---------------------------------------------------------------------------
+
+function Remove-InvalidFileNameChars
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [AllowEmptyString()]
+ [System.String]$Name
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce deprecation of function and set up accumulator for all piped in names.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Remove-ADTInvalidFileNameChars]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $names = [System.Collections.Specialized.StringCollection]::new()
+ }
+
+ process
+ {
+ # Add all non-null names to the collector.
+ if (![System.String]::IsNullOrWhiteSpace($Name))
+ {
+ $null = $names.Add($Name)
+ }
+ }
+
+ end
+ {
+ # Process provided names if we have any.
+ if ($names.Count)
+ {
+ try
+ {
+ $names | Remove-ADTInvalidFileNameChars
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTApplication
+#
+#---------------------------------------------------------------------------
+
+function Get-InstalledApplication
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Name', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ProductCode', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'IncludeUpdatesAndHotfixes', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ProductCode,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Exact,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$WildCard,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$RegEx,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeUpdatesAndHotfixes
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTApplication]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $gaiaParams = Get-ADTBoundParametersAndDefaultValues -Invocation $MyInvocation -Exclude Exact, WildCard, RegEx
+
+ if ($Exact)
+ {
+ $gaiaParams.NameMatch = 'Exact'
+ }
+ elseif ($WildCard)
+ {
+ $gaiaParams.NameMatch = 'WildCard'
+ }
+ elseif ($RegEx)
+ {
+ $gaiaParams.NameMatch = 'RegEx'
+ }
+
+ # Invoke execution.
+ try
+ {
+ Get-ADTApplication @gaiaParams
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Uninstall-ADTApplication
+#
+#---------------------------------------------------------------------------
+
+function Remove-MSIApplications
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Name', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidAssignmentToAutomaticVariable', '', Justification = '$_ is intentionally overwritten in this function to expand the input array.')]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ArgumentList', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'AdditionalArgumentList', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'IncludeUpdatesAndHotfixes', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'LoggingOptions', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'LogFileName', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'PassThru', Justification = "This parameter is passed to an underlying function via `$PSBoundParameters, therefore this warning is benign.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Exact', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'WildCard', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Exact,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$WildCard,
+
+ [Parameter(Mandatory = $false)]
+ [Alias('Arguments', 'Parameters')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('AddParameters')]
+ [System.String]$AdditionalArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Array]$FilterApplication,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Array]$ExcludeFromUninstall,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeUpdatesAndHotfixes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LoggingOptions,
+
+ [Parameter(Mandatory = $false)]
+ [Alias('LogName')]
+ [System.String]$LogFileName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Uninstall-ADTApplication]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Build out hashtable for splatting.
+ $uaaParams = Get-ADTBoundParametersAndDefaultValues -Invocation $MyInvocation -Exclude Exact, WildCard, FilterApplication, ExcludeFromUninstall, ContinueOnError
+ $uaaParams.ApplicationType = 'MSI'
+ if (!$ContinueOnError)
+ {
+ $uaaParams.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ # Build out filterscript based on provided input.
+ $filterArray = $(
+ $filterApplication | & {
+ process
+ {
+ if ($null -ne $_)
+ {
+ if ($_.Count -eq 1 -and $_[0].Count -eq 3) { $_ = $_[0] } # Handle the case where input is of the form @(, @('Prop', 'Value', 'Exact'), @('Prop', 'Value', 'Exact'))
+ if ($_[2] -eq 'RegEx')
+ {
+ "`$_.$($_[0]) -match '$($_[1] -replace "'","''")'"
+ }
+ elseif ($_[2] -eq 'Contains')
+ {
+ "`$_.$($_[0]) -match '$([System.Text.RegularExpressions.Regex]::Escape(($_[1] -replace "'","''")))'"
+ }
+ elseif ($_[2] -eq 'WildCard')
+ {
+ "`$_.$($_[0]) -like '$($_[1] -replace "'","''")'"
+ }
+ elseif ($_[2] -eq 'Exact')
+ {
+ if ($_[1] -is [System.Boolean])
+ {
+ "`$_.$($_[0]) -eq `$$($_[1].ToString().ToLower())"
+ }
+ else
+ {
+ "`$_.$($_[0]) -eq '$($_[1] -replace "'","''")'"
+ }
+ }
+ }
+ }
+ }
+ $excludeFromUninstall | & {
+ process
+ {
+ if ($null -ne $_)
+ {
+ if ($_.Count -eq 1 -and $_[0].Count -eq 3) { $_ = $_[0] } # Handle the case where input is of the form @(, @('Prop', 'Value', 'Exact'), @('Prop', 'Value', 'Exact'))
+ if ($_[2] -eq 'RegEx')
+ {
+ "`$_.$($_[0]) -notmatch '$($_[1] -replace "'","''")'"
+ }
+ elseif ($_[2] -eq 'Contains')
+ {
+ "`$_.$($_[0]) -notmatch '$([System.Text.RegularExpressions.Regex]::Escape(($_[1] -replace "'","''")))'"
+ }
+ elseif ($_[2] -eq 'WildCard')
+ {
+ "`$_.$($_[0]) -notlike '$($_[1] -replace "'","''")'"
+ }
+ elseif ($_[2] -eq 'Exact')
+ {
+ if ($_[1] -is [System.Boolean])
+ {
+ "`$_.$($_[0]) -ne `$$($_[1].ToString().ToLower())"
+ }
+ else
+ {
+ "`$_.$($_[0]) -ne '$($_[1] -replace "'","''")'"
+ }
+ }
+ }
+ }
+ }
+ )
+
+ $filterScript = $filterArray -join ' -and '
+
+ if ($filterScript)
+ {
+ $uaaParams.filterScript = [System.Management.Automation.ScriptBlock]::Create($filterScript)
+ }
+
+ # Invoke execution.
+ try
+ {
+ Uninstall-ADTApplication @uaaParams
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTFileVersion
+#
+#---------------------------------------------------------------------------
+
+function Get-FileVersion
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$File,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ProductVersion,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTFileVersion]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ try
+ {
+ Get-ADTFileVersion @PSBoundParameters
+ }
+ catch
+ {
+ Write-ADTLogEntry -Message "Failed to get version info.`n$(Resolve-ADTErrorRecord -ErrorRecord $_)" -Severity 3
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTUserProfiles
+#
+#---------------------------------------------------------------------------
+
+function Get-UserProfiles
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ExcludeNTAccount,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExcludeSystemProfiles = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExcludeServiceProfiles = $true,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeDefaultUser
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Translate parameters.
+ $null = ('SystemProfiles', 'ServiceProfiles').Where({ $PSBoundParameters.ContainsKey("Exclude$_") }).ForEach({
+ if (!$PSBoundParameters."Exclude$_")
+ {
+ $PSBoundParameters.Add("Include$_", [System.Management.Automation.SwitchParameter]$true)
+ }
+ $PSBoundParameters.Remove("Exclude$_")
+ })
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTUserProfiles]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Get-ADTUserProfiles @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Update-ADTDesktop
+#
+#---------------------------------------------------------------------------
+
+function Update-Desktop
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Update-ADTDesktop]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Update-ADTDesktop
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Update-ADTEnvironmentPsProvider
+#
+#---------------------------------------------------------------------------
+
+function Update-SessionEnvironmentVariables
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [System.Management.Automation.SwitchParameter]$LoadLoggedOnUserEnvironmentVariables,
+
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Update-ADTEnvironmentPsProvider]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Update-ADTEnvironmentPsProvider -LoadLoggedOnUserEnvironmentVariables:$LoadLoggedOnUserEnvironmentVariables
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Copy-ADTFile
+#
+#---------------------------------------------------------------------------
+
+function Copy-File
+{
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Destination,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Flatten,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueFileCopyOnError = $false,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$UseRobocopy,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$RobocopyParams = '/NJH /NJS /NS /NC /NP /NDL /FP /IS /IT /IM /XX /MT:4 /R:1 /W:1',
+
+ [Parameter(Mandatory = $false)]
+ [System.String]$RobocopyAdditionalParams
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Copy-ADTFile]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ if (!$UseRobocopy)
+ {
+ if ($PSBoundParameters.ContainsKey('RobocopyParams'))
+ {
+ $null = $PSBoundParameters.Remove('RobocopyParams')
+ }
+ if ($PSBoundParameters.ContainsKey('RobocopyAdditionalParams'))
+ {
+ $null = $PSBoundParameters.Remove('RobocopyAdditionalParams')
+ }
+ }
+ if ($PSBoundParameters.ContainsKey('UseRobocopy'))
+ {
+ $null = $PSBoundParameters.Add('FileCopyMode', ('Native', 'Robocopy')[$PSBoundParameters.UseRobocopy])
+ $null = $PSBoundParameters.Remove('UseRobocopy')
+ }
+ try
+ {
+ Copy-ADTFile @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Remove-ADTFile
+#
+#---------------------------------------------------------------------------
+
+function Remove-File
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'LiteralPath')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$LiteralPath,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Remove-ADTFile]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Remove-ADTFile @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Copy-ADTFileToUserProfiles
+#
+#---------------------------------------------------------------------------
+
+function Copy-FileToUserProfiles
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true)]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [System.String]$Destination,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Flatten,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$UseRobocopy,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$RobocopyAdditionalParams,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ExcludeNTAccount,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExcludeSystemProfiles = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExcludeServiceProfiles = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$ExcludeDefaultUser,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueFileCopyOnError
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Copy-ADTFileToUserProfiles]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $null = ('SystemProfiles', 'ServiceProfiles').Where({ $PSBoundParameters.ContainsKey("Exclude$_") }).ForEach({
+ if (!$PSBoundParameters."Exclude$_")
+ {
+ $PSBoundParameters.Add("Include$_", [System.Management.Automation.SwitchParameter]$true)
+ }
+ $PSBoundParameters.Remove("Exclude$_")
+ })
+ if ($PSBoundParameters.ContainsKey('UseRobocopy'))
+ {
+ $PSBoundParameters.Add('FileCopyMode', ('Native', 'Robocopy')[$PSBoundParameters.UseRobocopy])
+ $null = $PSBoundParameters.Remove('UseRobocopy')
+ }
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ # Set up collector for piped in path objects.
+ $srcPaths = [System.Collections.Specialized.StringCollection]::new()
+ }
+
+ process
+ {
+ # Add all non-null strings to the collector.
+ $null = $Path | & {
+ process
+ {
+ if (![System.String]::IsNullOrWhiteSpace($_))
+ {
+ $srcPaths.Add($_)
+ }
+ }
+ }
+ }
+
+ end
+ {
+ # Process provided paths if we have any.
+ if ($srcPaths.Count)
+ {
+ try
+ {
+ $PSBoundParameters.Path = $srcPaths
+ Copy-ADTFileToUserProfiles @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Show-ADTInstallationPrompt
+#
+#---------------------------------------------------------------------------
+
+function Show-InstallationPrompt
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Message,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Left', 'Center', 'Right')]
+ [System.String]$MessageAlignment,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonRightText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonLeftText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonMiddleText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Application', 'Asterisk', 'Error', 'Exclamation', 'Hand', 'Information', 'None', 'Question', 'Shield', 'Warning', 'WinLogo')]
+ [System.String]$Icon,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PersistPrompt,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$MinimizeWindows,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$Timeout,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExitOnTimeout,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$TopMost
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Show-ADTInstallationPrompt]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Tune up parameters. A lot has changed.
+ if ($PSBoundParameters.ContainsKey('Icon') -and ($PSBoundParameters.Icon -eq 'None'))
+ {
+ $null = $PSBoundParameters.Remove('Icon')
+ }
+ if ($PSBoundParameters.ContainsKey('ExitOnTimeout'))
+ {
+ $PSBoundParameters.Add('NoExitOnTimeout', !$PSBoundParameters.ExitOnTimeout)
+ $null = $PSBoundParameters.Remove('ExitOnTimeout')
+ }
+ if ($PSBoundParameters.ContainsKey('TopMost'))
+ {
+ $PSBoundParameters.Add('NotTopMost', !$PSBoundParameters.TopMost)
+ $null = $PSBoundParameters.Remove('TopMost')
+ }
+
+ # Invoke function with amended parameters.
+ try
+ {
+ Show-ADTInstallationPrompt @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Show-ADTInstallationProgress
+#
+#---------------------------------------------------------------------------
+
+function Show-InstallationProgress
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$StatusMessage,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Default', 'TopLeft', 'Top', 'TopRight', 'TopCenter', 'BottomLeft', 'Bottom', 'BottomRight')]
+ [System.String]$WindowLocation,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$TopMost = $true,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Quiet,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoRelocation
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Show-ADTInstallationProgress]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('TopMost'))
+ {
+ $PSBoundParameters.Add('NotTopMost', !$PSBoundParameters.TopMost)
+ $null = $PSBoundParameters.Remove('TopMost')
+ }
+ if ($PSBoundParameters.ContainsKey('Quiet'))
+ {
+ $PSBoundParameters.Add('InformationAction', [System.Management.Automation.ActionPreference]::SilentlyContinue)
+ $null = $PSBoundParameters.Remove('Quiet')
+ }
+ try
+ {
+ Show-ADTInstallationProgress @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Show-ADTDialogBox
+#
+#---------------------------------------------------------------------------
+
+function Show-DialogBox
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Enter a message for the dialog box.')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Text,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('OK', 'OKCancel', 'AbortRetryIgnore', 'YesNoCancel', 'YesNo', 'RetryCancel', 'CancelTryAgainContinue')]
+ [System.String]$Buttons,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('First', 'Second', 'Third')]
+ [System.String]$DefaultButton,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Exclamation', 'Information', 'None', 'Stop', 'Question')]
+ [System.String]$Icon,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Timeout,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$TopMost
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Show-ADTDialogBox]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('TopMost'))
+ {
+ $PSBoundParameters.Add('NotTopMost', !$PSBoundParameters.TopMost)
+ $null = $PSBoundParameters.Remove('TopMost')
+ }
+ try
+ {
+ Show-ADTDialogBox @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Show-ADTInstallationWelcome
+#
+#---------------------------------------------------------------------------
+
+function Show-InstallationWelcome
+{
+ [CmdletBinding(DefaultParameterSetName = 'None')]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$CloseApps,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Silent,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$CloseAppsCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$ForceCloseAppsCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PromptToSave,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PersistPrompt,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$BlockExecution,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$AllowDefer,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$AllowDeferCloseApps,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$DeferTimes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$DeferDays,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DeferDeadline,
+
+ [Parameter(ParameterSetName = 'CheckDiskSpaceParameterSet', Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$CheckDiskSpace,
+
+ [Parameter(ParameterSetName = 'CheckDiskSpaceParameterSet', Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$RequiredDiskSpace,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$MinimizeWindows = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$TopMost = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$ForceCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$CustomText
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Show-ADTInstallationWelcome]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Tune up parameters. A lot has changed.
+ if ($PSBoundParameters.ContainsKey('CloseApps'))
+ {
+ $PSBoundParameters.CloseProcesses = $CloseApps.Split(',') | & {
+ process
+ {
+ $name, $description = $_.Split('=')
+ if ($description)
+ {
+ return [PSADT.Types.ProcessObject]::new($name, $description)
+ }
+ else
+ {
+ return [PSADT.Types.ProcessObject]::new($name)
+ }
+ }
+ }
+ $null = $PSBoundParameters.Remove('CloseApps')
+ }
+ $null = ('{0}Countdown', 'Force{0}Countdown', 'AllowDefer{0}').ForEach({
+ if ($PSBoundParameters.ContainsKey(($oldParam = [System.String]::Format($_, 'CloseApps'))))
+ {
+ $PSBoundParameters.Add([System.String]::Format($_, 'CloseProcesses'), $PSBoundParameters.$oldParam)
+ $PSBoundParameters.Remove($oldParam)
+ }
+ })
+ if ($PSBoundParameters.ContainsKey('MinimizeWindows'))
+ {
+ $PSBoundParameters.Add('NoMinimizeWindows', !$PSBoundParameters.MinimizeWindows)
+ $null = $PSBoundParameters.Remove('MinimizeWindows')
+ }
+ if ($PSBoundParameters.ContainsKey('TopMost'))
+ {
+ $PSBoundParameters.Add('NotTopMost', !$PSBoundParameters.TopMost)
+ $null = $PSBoundParameters.Remove('TopMost')
+ }
+
+ # Invoke function with amended parameters.
+ try
+ {
+ Show-ADTInstallationWelcome @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTWindowTitle
+#
+#---------------------------------------------------------------------------
+
+function Get-WindowTitle
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'SearchWinTitle')]
+ [AllowEmptyString()]
+ [System.String]$WindowTitle,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'GetAllWinTitles')]
+ [System.Management.Automation.SwitchParameter]$GetAllWindowTitles,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DisableFunctionLogging
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTWindowTitle]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('DisableFunctionLogging'))
+ {
+ $PSBoundParameters.Add('InformationAction', [System.Management.Automation.ActionPreference]::SilentlyContinue)
+ $null = $PSBoundParameters.Remove('DisableFunctionLogging')
+ }
+ try
+ {
+ Get-ADTWindowTitle @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Show-ADTInstallationRestartPrompt
+#
+#---------------------------------------------------------------------------
+
+function Show-InstallationRestartPrompt
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$CountdownSeconds,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$CountdownNoHideSeconds,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$NoSilentRestart = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$SilentCountdownSeconds,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$TopMost = $true,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoCountdown
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Show-ADTInstallationRestartPrompt]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('NoSilentRestart'))
+ {
+ $PSBoundParameters.Add('SilentRestart', !$PSBoundParameters.NoSilentRestart)
+ $null = $PSBoundParameters.Remove('NoSilentRestart')
+ }
+ if ($PSBoundParameters.ContainsKey('TopMost'))
+ {
+ $PSBoundParameters.Add('NotTopMost', !$PSBoundParameters.TopMost)
+ $null = $PSBoundParameters.Remove('TopMost')
+ }
+ try
+ {
+ Show-ADTInstallationRestartPrompt @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Show-ADTBalloonTip
+#
+#---------------------------------------------------------------------------
+
+function Show-BalloonTip
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$BalloonTipText,
+
+ [Parameter(Mandatory = $false, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$BalloonTipTitle,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateSet('Error', 'Info', 'None', 'Warning')]
+ [System.Windows.Forms.ToolTipIcon]$BalloonTipIcon,
+
+ [Parameter(Mandatory = $false, Position = 3)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$BalloonTipTime,
+
+ [Parameter(Mandatory = $false, Position = 4)]
+ [System.Management.Automation.SwitchParameter]$NoWait
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Show-ADTBalloonTip]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($NoWait)
+ {
+ Write-ADTLogEntry -Message "The parameter '-NoWait' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $null = $PSBoundParameters.Remove('NoWait')
+ }
+ try
+ {
+ Show-ADTBalloonTip @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Copy-ADTContentToCache
+#
+#---------------------------------------------------------------------------
+
+function Copy-ContentToCache
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false, Position = 0, HelpMessage = 'The path to the software cache folder')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Copy-ADTContentToCache]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Copy-ADTContentToCache @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Remove-ADTContentFromCache
+#
+#---------------------------------------------------------------------------
+
+function Remove-ContentFromCache
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $false, Position = 0, HelpMessage = 'The path to the software cache folder')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Remove-ADTContentFromCache]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Remove-ADTContentFromCache @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Test-ADTNetworkConnection
+#
+#---------------------------------------------------------------------------
+
+function Test-NetworkConnection
+{
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Test-ADTNetworkConnection]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Test-ADTNetworkConnection
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTLoggedOnUser
+#
+#---------------------------------------------------------------------------
+
+function Get-LoggedOnUser
+{
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTLoggedOnUser]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Get-ADTLoggedOnUser
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTIniValue
+#
+#---------------------------------------------------------------------------
+
+function Get-IniValue
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((New-ADTValidateScriptErrorRecord -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Section,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTIniValue]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ try
+ {
+ Get-ADTIniValue @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTIniValue
+#
+#---------------------------------------------------------------------------
+
+function Set-IniValue
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((New-ADTValidateScriptErrorRecord -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Section,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [System.Object]$Value,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTIniValue]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ try
+ {
+ Set-ADTIniValue @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around New-ADTFolder
+#
+#---------------------------------------------------------------------------
+
+function New-Folder
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [New-ADTFolder]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ New-ADTFolder @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Test-ADTPowerPoint
+#
+#---------------------------------------------------------------------------
+
+function Test-PowerPoint
+{
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Test-PowerPoint]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Test-ADTPowerPoint
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Update-ADTGroupPolicy
+#
+#---------------------------------------------------------------------------
+
+function Update-GroupPolicy
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Update-ADTGroupPolicy]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Update-ADTGroupPolicy @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTUniversalDate
+#
+#---------------------------------------------------------------------------
+
+function Get-UniversalDate
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DateTime,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $false
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTUniversalDate]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ try
+ {
+ Get-ADTUniversalDate @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Test-ADTServiceExists
+#
+#---------------------------------------------------------------------------
+
+function Test-ServiceExists
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ComputerName,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Test-ADTServiceExists]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($ComputerName)
+ {
+ Write-ADTLogEntry -Message "The parameter '-ComputerName' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $null = $PSBoundParameters.Remove('ComputerName')
+ }
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ try
+ {
+ Test-ADTServiceExists @PSBoundParameters -UseCIM
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Disable-ADTTerminalServerInstallMode
+#
+#---------------------------------------------------------------------------
+
+function Disable-TerminalServerInstallMode
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Disable-ADTTerminalServerInstallMode]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Disable-ADTTerminalServerInstallMode @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Disable-ADTTerminalServerInstallMode
+#
+#---------------------------------------------------------------------------
+
+function Enable-TerminalServerInstallMode
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Enable-ADTTerminalServerInstallMode]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Enable-ADTTerminalServerInstallMode @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Add-ADTEdgeExtension and Remove-ADTEdgeExtension
+#
+#---------------------------------------------------------------------------
+
+function Configure-EdgeExtension
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Add')]
+ [System.Management.Automation.SwitchParameter]$Add,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Remove')]
+ [System.Management.Automation.SwitchParameter]$Remove,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Add')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'Remove')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ExtensionID,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Add')]
+ [ValidateSet('blocked', 'allowed', 'removed', 'force_installed', 'normal_installed')]
+ [System.String]$InstallationMode,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Add')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$UpdateUrl,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Add')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$MinimumVersionRequired
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [$($PSCmdlet.ParameterSetName)-ADTEdgeExtension]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $null = $PSBoundParameters.Remove($PSCmdlet.ParameterSetName)
+ try
+ {
+ & "$($PSCmdlet.ParameterSetName)-ADTEdgeExtension" @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Resolve-ADTErrorRecord
+#
+#---------------------------------------------------------------------------
+
+function Resolve-Error
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidDefaultValueSwitchParameter', '', Justification = "This compatibility layer has several switches defaulting to True out of necessity for supporting PSAppDeployToolit 3.x Deploy-Application.ps1 scripts.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [AllowEmptyCollection()]
+ [System.Array]$ErrorRecord,
+
+ [Parameter(Mandatory = $false, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Property,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [System.Management.Automation.SwitchParameter]$GetErrorRecord = $true,
+
+ [Parameter(Mandatory = $false, Position = 3)]
+ [System.Management.Automation.SwitchParameter]$GetErrorInvocation = $true,
+
+ [Parameter(Mandatory = $false, Position = 4)]
+ [System.Management.Automation.SwitchParameter]$GetErrorException = $true,
+
+ [Parameter(Mandatory = $false, Position = 5)]
+ [System.Management.Automation.SwitchParameter]$GetErrorInnerException = $true
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate bad switches before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Resolve-ADTErrorRecord]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $null = ('ErrorRecord', 'ErrorInvocation', 'ErrorException', 'ErrorInnerException').Where({ $PSBoundParameters.ContainsKey($_) }).ForEach({
+ $PSBoundParameters.Add("Exclude$_", !$PSBoundParameters."Get$_")
+ $PSBoundParameters.Remove("Get$_")
+ })
+
+ # Set up collector for piped in ErrorRecord objects.
+ $errRecords = [System.Collections.Generic.List[System.Management.Automation.ErrorRecord]]::new()
+ }
+
+ process
+ {
+ # Process piped input and collect ErrorRecord objects.
+ $ErrorRecord | & {
+ process
+ {
+ if ($_ -is [System.Management.Automation.ErrorRecord])
+ {
+ $errRecords.Add($_)
+ }
+ }
+ }
+ }
+
+ end
+ {
+ # Process the collected ErrorRecord objects.
+ try
+ {
+ # If we've collected no ErrorRecord objects, choose the latest error that occurred.
+ if (!$errRecords.Count)
+ {
+ if (($errRecord = Get-Variable -Name PSItem -Scope 1 -ValueOnly -ErrorAction Ignore) -and ($errRecord -is [System.Management.Automation.ErrorRecord]))
+ {
+ $errRecord | Resolve-ADTErrorRecord @PSBoundParameters
+ }
+ elseif ($Global:Error.Count)
+ {
+ $Global:Error.Where({ $_ -is [System.Management.Automation.ErrorRecord] }, 'First', 1) | Resolve-ADTErrorRecord @PSBoundParameters
+ }
+ }
+ else
+ {
+ if ($PSBoundParameters.ContainsKey('ErrorRecord'))
+ {
+ $null = $PSBoundParameters.Remove('ErrorRecord')
+ }
+ $errRecords | Resolve-ADTErrorRecord @PSBoundParameters
+ }
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTServiceStartMode
+#
+#---------------------------------------------------------------------------
+
+function Get-ServiceStartMode
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Name')]
+ [System.String]$Service,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ComputerName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTServiceStartMode]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($ComputerName)
+ {
+ Write-ADTLogEntry -Message "The parameter '-ComputerName' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $null = $PSBoundParameters.Remove('ComputerName')
+ }
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ try
+ {
+ Get-ADTServiceStartMode @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTServiceStartMode
+#
+#---------------------------------------------------------------------------
+
+function Set-ServiceStartMode
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Name')]
+ [System.String]$Service,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$StartMode,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTServiceStartMode]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ try
+ {
+ Set-ADTServiceStartMode @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Start-ADTProcess
+#
+#---------------------------------------------------------------------------
+
+function Execute-Process
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [Alias('Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [Alias('Arguments', 'Parameters')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [Alias('SecureParameters')]
+ [System.Management.Automation.SwitchParameter]$SecureArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Normal', 'Hidden', 'Maximized', 'Minimized')]
+ [System.Diagnostics.ProcessWindowStyle]$WindowStyle,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$CreateNoWindow,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$WaitForMsiExec,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$MsiExecWaitTime = (Get-ADTConfig).MSI.MutexWaitTime,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$IgnoreExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Idle', 'Normal', 'High', 'AboveNormal', 'BelowNormal', 'RealTime')]
+ [System.Diagnostics.ProcessPriorityClass]$PriorityClass,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExitOnProcessFailure = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$UseShellExecute,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $false
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce deprecation of this function.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Start-ADTProcess]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Convert out changed parameters.
+ if ($PSBoundParameters.ContainsKey('IgnoreExitCodes'))
+ {
+ $PSBoundParameters.IgnoreExitCodes = $IgnoreExitCodes.Split(',')
+ }
+ if ($PSBoundParameters.ContainsKey('ContinueOnError') -or $PSBoundParameters.ContainsKey('ExitOnProcessFailure'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ $null = $PSBoundParameters.Remove('ExitOnProcessFailure')
+ $PSBoundParameters.ErrorAction = ([System.Management.Automation.ActionPreference]::Stop, [System.Management.Automation.ActionPreference]::SilentlyContinue)[$ContinueOnError -or !$ExitOnProcessFailure]
+ }
+
+ # Invoke function with amended parameters.
+ try
+ {
+ Start-ADTProcess @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Start-ADTMsiProcess
+#
+#---------------------------------------------------------------------------
+
+function Execute-MSI
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Install', 'Uninstall', 'Patch', 'Repair', 'ActiveSetup')]
+ [System.String]$Action,
+
+ [Parameter(Mandatory = $true, HelpMessage = 'Please enter either the path to the MSI/MSP file or the ProductCode')]
+ [ValidateScript({ ($_ -match (Get-ADTEnvironmentTable).MSIProductCodeRegExPattern) -or ('.msi', '.msp' -contains [System.IO.Path]::GetExtension($_)) })]
+ [Alias('Path')]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Transform,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Arguments', 'Parameters')]
+ [System.String]$ArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('AddParameters')]
+ [System.String]$AdditionalArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [Alias('SecureParameters')]
+ [System.Management.Automation.SwitchParameter]$SecureArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Patch,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LoggingOptions,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('LogName')]
+ [System.String]$LogFileName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipMSIAlreadyInstalledCheck,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeUpdatesAndHotfixes,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$IgnoreExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Idle', 'Normal', 'High', 'AboveNormal', 'BelowNormal', 'RealTime')]
+ [Diagnostics.ProcessPriorityClass]$PriorityClass,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExitOnProcessFailure = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$RepairFromSource,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $false
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce deprecation of this function.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Start-ADTMsiProcess]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Convert out changed parameters.
+ if ($FilePath -match (Get-ADTEnvironmentTable).MSIProductCodeRegExPattern)
+ {
+ $PSBoundParameters.ProductCode = [System.Guid]::new($FilePath)
+ $null = $PSBoundParameters.Remove('FilePath')
+ }
+ if ($PSBoundParameters.ContainsKey('Transform'))
+ {
+ $PSBoundParameters.Transforms = $Transform.Split(';')
+ $null = $PSBoundParameters.Remove('Transform')
+ }
+ if ($PSBoundParameters.ContainsKey('IgnoreExitCodes'))
+ {
+ $PSBoundParameters.IgnoreExitCodes = $IgnoreExitCodes.Split(',')
+ }
+ if ($PSBoundParameters.ContainsKey('ContinueOnError') -or $PSBoundParameters.ContainsKey('ExitOnProcessFailure'))
+ {
+ $PSBoundParameters.ErrorAction = ([System.Management.Automation.ActionPreference]::Stop, [System.Management.Automation.ActionPreference]::SilentlyContinue)[$ContinueOnError -or !$ExitOnProcessFailure]
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ $null = $PSBoundParameters.Remove('ExitOnProcessFailure')
+ }
+
+ # Invoke function with amended parameters.
+ try
+ {
+ Start-ADTMsiProcess @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Start-ADTMspProcess
+#
+#---------------------------------------------------------------------------
+
+function Execute-MSP
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, HelpMessage = 'Please enter the path to the MSP file')]
+ [ValidateScript({ ('.msp' -contains [System.IO.Path]::GetExtension($_)) })]
+ [Alias('Path')]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$AddParameters
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Start-ADTMspProcess]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Start-ADTMspProcess @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Unblock-ADTAppExecution
+#
+#---------------------------------------------------------------------------
+
+function Unblock-AppExecution
+{
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Unblock-ADTAppExecution]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Unblock-ADTAppExecution
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Block-ADTAppExecution
+#
+#---------------------------------------------------------------------------
+
+function Block-AppExecution
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, HelpMessage = 'Specify process names, separated by commas.')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ProcessName
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Block-ADTAppExecution]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Block-ADTAppExecution @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Test-ADTRegistryValue
+#
+#---------------------------------------------------------------------------
+
+function Test-RegistryValue
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$Key,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Value')]
+ [System.Object]$Name,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce deprecation of function and set up accumulator for all piped in keys.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Test-ADTRegistryValue]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $keys = [System.Collections.Generic.List[System.Object]]::new()
+ }
+
+ process
+ {
+ # Add all keys to the collector.
+ $keys.Add($Key)
+ }
+
+ end
+ {
+ # Process provided keys if we have any.
+ if ($keys.Count)
+ {
+ try
+ {
+ if ($PSBoundParameters.ContainsKey('Key'))
+ {
+ $null = $PSBoundParameters.Remove('Key')
+ }
+ $keys | Test-ADTRegistryValue @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Convert-ADTRegistryPath
+#
+#---------------------------------------------------------------------------
+
+function Convert-RegistryPath
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$DisableFunctionLogging = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Convert-ADTRegistryPath]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('DisableFunctionLogging'))
+ {
+ $null = $PSBoundParameters.Remove('DisableFunctionLogging')
+ }
+ if (!$DisableFunctionLogging)
+ {
+ $PSBoundParameters.Add('InformationAction', [System.Management.Automation.ActionPreference]::Continue)
+ }
+ try
+ {
+ Convert-ADTRegistryPath @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Test-ADTMSUpdates
+#
+#---------------------------------------------------------------------------
+
+function Test-MSUpdates
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Enter the KB Number for the Microsoft Update')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$KbNumber,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Test-ADTMSUpdates]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Test-ADTMSUpdates @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Test-ADTBattery
+#
+#---------------------------------------------------------------------------
+
+function Test-Battery
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Test-ADTBattery]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Test-ADTBattery @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Start-ADTServiceAndDependencies
+#
+#---------------------------------------------------------------------------
+
+function Start-ServiceAndDependencies
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Name')]
+ [System.String]$Service,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ComputerName,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipServiceExistsTest,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipDependentServices,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$PendingStatusWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and dead parameters.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Start-ADTServiceAndDependencies]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $null = ('ComputerName', 'SkipServiceExistsTest').ForEach({
+ if ($PSBoundParameters.ContainsKey($_))
+ {
+ Write-ADTLogEntry -Message "The parameter '-$_' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $PSBoundParameters.Remove($_)
+ }
+ })
+
+ # Translate $ContinueOnError to an ActionPreference before executing.
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ try
+ {
+ Start-ADTServiceAndDependencies @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Stop-ADTServiceAndDependencies
+#
+#---------------------------------------------------------------------------
+
+function Stop-ServiceAndDependencies
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Name')]
+ [System.String]$Service,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ComputerName,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipServiceExistsTest,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipDependentServices,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$PendingStatusWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and dead parameters.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Stop-ADTServiceAndDependencies]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $null = ('ComputerName', 'SkipServiceExistsTest').ForEach({
+ if ($PSBoundParameters.ContainsKey($_))
+ {
+ Write-ADTLogEntry -Message "The parameter '-$_' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $PSBoundParameters.Remove($_)
+ }
+ })
+
+ # Translate $ContinueOnError to an ActionPreference before executing.
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ try
+ {
+ Stop-ADTServiceAndDependencies @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTRegistryKey
+#
+#---------------------------------------------------------------------------
+
+function Set-RegistryKey
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Object]$Value,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Binary', 'DWord', 'ExpandString', 'MultiString', 'None', 'QWord', 'String', 'Unknown')]
+ [Microsoft.Win32.RegistryValueKind]$Type,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTRegistryKey]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Set-ADTRegistryKey @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Remove-ADTRegistryKey
+#
+#---------------------------------------------------------------------------
+
+function Remove-RegistryKey
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Remove-ADTRegistryKey]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Remove-ADTRegistryKey @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Remove-ADTFileFromUserProfiles
+#
+#---------------------------------------------------------------------------
+
+function Remove-FileFromUserProfiles
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'LiteralPath')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$LiteralPath,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ExcludeNTAccount,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExcludeSystemProfiles = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExcludeServiceProfiles = $true,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeDefaultUser,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and dead parameters.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Remove-ADTFileFromUserProfiles]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ $null = ('SystemProfiles', 'ServiceProfiles').Where({ $PSBoundParameters.ContainsKey("Exclude$_") }).ForEach({
+ if (!$PSBoundParameters."Exclude$_")
+ {
+ $PSBoundParameters.Add("Include$_", [System.Management.Automation.SwitchParameter]$true)
+ }
+ $PSBoundParameters.Remove("Exclude$_")
+ })
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ Write-ADTLogEntry -Message "The parameter '-ContinueOnError' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+
+ try
+ {
+ Remove-ADTFileFromUserProfiles @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTRegistryKey
+#
+#---------------------------------------------------------------------------
+
+function Get-RegistryKey
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Value')]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ReturnEmptyKeyIfExists,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DoNotExpandEnvironmentNames,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTRegistryKey]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Get-ADTRegistryKey @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Install-ADTMSUpdates
+#
+#---------------------------------------------------------------------------
+
+function Install-MSUpdates
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Directory
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Install-ADTMSUpdates]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Install-ADTMSUpdates @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTSchedulerTask
+#
+#---------------------------------------------------------------------------
+
+function Get-SchedulerTask
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TaskName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTSchedulerTask]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Get-ADTSchedulerTask @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTPendingReboot
+#
+#---------------------------------------------------------------------------
+
+function Get-PendingReboot
+{
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTPendingReboot]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ try
+ {
+ Get-ADTPendingReboot
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Invoke-ADTRegSvr32
+#
+#---------------------------------------------------------------------------
+
+function Invoke-RegisterOrUnregisterDLL
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Register', 'Unregister')]
+ [Alias('DLLAction')]
+ [System.String]$Action,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Invoke-ADTRegSvr32]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Invoke-ADTRegSvr32 @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Register-ADTDll
+#
+#---------------------------------------------------------------------------
+
+function Register-DLL
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Register-ADTDll]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Register-ADTDll @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Unregister-ADTDll
+#
+#---------------------------------------------------------------------------
+
+function Unregister-DLL
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Unregister-ADTDll]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Unregister-ADTDll @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Remove-ADTFolder
+#
+#---------------------------------------------------------------------------
+
+function Remove-Folder
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DisableRecursion,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Remove-ADTFolder]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Remove-ADTFolder @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTActiveSetup
+#
+#---------------------------------------------------------------------------
+
+function Set-ActiveSetup
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$StubExePath,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Arguments,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Description,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Version,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Locale,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [System.Management.Automation.SwitchParameter]$DisableActiveSetup,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Purge')]
+ [System.Management.Automation.SwitchParameter]$PurgeActiveSetupKey,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ExecuteForCurrentUser = $true,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTActiveSetup]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ExecuteForCurrentUser'))
+ {
+ $PSBoundParameters.Add('NoExecuteForCurrentUser', !$PSBoundParameters.ExecuteForCurrentUser)
+ $null = $PSBoundParameters.Remove('ExecuteForCurrentUser')
+ }
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if ($StubExePath.EndsWith('.ps1'))
+ {
+ $PSBoundParameters.Add('ExecutionPolicy', [Microsoft.PowerShell.ExecutionPolicy]::Bypass)
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Set-ADTActiveSetup @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTItemPermission
+#
+#---------------------------------------------------------------------------
+
+function Set-ItemPermission
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Path to the folder or file you want to modify (ex: C:\Temp)', ParameterSetName = 'DisableInheritance')]
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Path to the folder or file you want to modify (ex: C:\Temp)', ParameterSetName = 'EnableInheritance')]
+ [ValidateNotNullOrEmpty()]
+ [Alias('File', 'Folder')]
+ [System.String]$Path,
+
+ [Parameter( Mandatory = $true, Position = 1, HelpMessage = 'One or more user names (ex: BUILTIN\Users, DOMAIN\Admin). If you want to use SID, prefix it with an asterisk * (ex: *S-1-5-18)', ParameterSetName = 'DisableInheritance')]
+ [Alias('Username', 'Users', 'SID', 'Usernames')]
+ [System.String[]]$User,
+
+ [Parameter( Mandatory = $true, Position = 2, HelpMessage = "Permission or list of permissions to be set/added/removed/replaced. To see all the possible permissions go to 'http://technet.microsoft.com/fr-fr/library/ff730951.aspx'", ParameterSetName = 'DisableInheritance')]
+ [Alias('Acl', 'Grant', 'Permissions', 'Deny')]
+ [ValidateSet('AppendData', 'ChangePermissions', 'CreateDirectories', 'CreateFiles', 'Delete', `
+ 'DeleteSubdirectoriesAndFiles', 'ExecuteFile', 'FullControl', 'ListDirectory', 'Modify', `
+ 'Read', 'ReadAndExecute', 'ReadAttributes', 'ReadData', 'ReadExtendedAttributes', 'ReadPermissions', `
+ 'Synchronize', 'TakeOwnership', 'Traverse', 'Write', 'WriteAttributes', 'WriteData', 'WriteExtendedAttributes', 'None')]
+ [System.String[]]$Permission,
+
+ [Parameter(Mandatory = $false, Position = 3, HelpMessage = 'Whether you want to set Allow or Deny permissions', ParameterSetName = 'DisableInheritance')]
+ [Alias('AccessControlType')]
+ [ValidateSet('Allow', 'Deny')]
+ [System.String]$PermissionType = 'Allow',
+
+ [Parameter(Mandatory = $false, Position = 4, HelpMessage = 'Sets how permissions are inherited', ParameterSetName = 'DisableInheritance')]
+ [ValidateSet('ContainerInherit', 'None', 'ObjectInherit')]
+ [System.String[]]$Inheritance = 'None',
+
+ [Parameter(Mandatory = $false, Position = 5, HelpMessage = 'Sets how to propage inheritance flags', ParameterSetName = 'DisableInheritance')]
+ [ValidateSet('None', 'InheritOnly', 'NoPropagateInherit')]
+ [System.String]$Propagation = 'None',
+
+ [Parameter(Mandatory = $false, Position = 6, HelpMessage = 'Specifies which method will be used to add/remove/replace permissions.', ParameterSetName = 'DisableInheritance')]
+ [ValidateSet('Add', 'Set', 'Reset', 'Remove', 'RemoveSpecific', 'RemoveAll')]
+ [Alias('ApplyMethod', 'ApplicationMethod')]
+ [System.String]$Method = 'Add',
+
+ [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'Enables inheritance, which removes explicit permissions.', ParameterSetName = 'EnableInheritance')]
+ [System.Management.Automation.SwitchParameter]$EnableInheritance
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTItemPermission]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('Method'))
+ {
+ $PSBoundParameters.Method = $PSBoundParameters.Method -replace '^(Add|Set|Reset|Remove)(Specific|All)?$', '$1AccessRule$2'
+ }
+ try
+ {
+ Set-ADTItemPermission @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around New-ADTMsiTransform
+#
+#---------------------------------------------------------------------------
+
+function New-MsiTransform
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$MsiPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ApplyTransformPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$NewTransformPath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Hashtable]$TransformProperties,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [New-ADTMsiTransform]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ New-ADTMsiTransform @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Invoke-ADTSCCMTask
+#
+#---------------------------------------------------------------------------
+
+function Invoke-SCCMTask
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('HardwareInventory', 'SoftwareInventory', 'HeartbeatDiscovery', 'SoftwareInventoryFileCollection', 'RequestMachinePolicy', 'EvaluateMachinePolicy', 'LocationServicesCleanup', 'SoftwareMeteringReport', 'SourceUpdate', 'PolicyAgentCleanup', 'RequestMachinePolicy2', 'CertificateMaintenance', 'PeerDistributionPointStatus', 'PeerDistributionPointProvisioning', 'ComplianceIntervalEnforcement', 'SoftwareUpdatesAgentAssignmentEvaluation', 'UploadStateMessage', 'StateMessageManager', 'SoftwareUpdatesScan', 'AMTProvisionCycle', 'UpdateStorePolicy', 'StateSystemBulkSend', 'ApplicationManagerPolicyAction', 'PowerManagementStartSummarizer')]
+ [System.String]$ScheduleID,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Invoke-ADTSCCMTask]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Invoke-ADTSCCMTask @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Install-ADTSCCMSoftwareUpdates
+#
+#---------------------------------------------------------------------------
+
+function Install-SCCMSoftwareUpdates
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$SoftwareUpdatesScanWaitInSeconds,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$WaitForPendingUpdatesTimeout,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Install-ADTSCCMSoftwareUpdates]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Install-ADTSCCMSoftwareUpdates @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Send-ADTKeys
+#
+#---------------------------------------------------------------------------
+
+function Send-Keys
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false, Position = 0)]
+ [AllowEmptyString()]
+ [ValidateNotNull()]
+ [System.String]$WindowTitle,
+
+ [Parameter(Mandatory = $false, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$GetAllWindowTitles,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateNotNullOrEmpty()]
+ [System.IntPtr]$WindowHandle,
+
+ [Parameter(Mandatory = $false, Position = 3)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Keys,
+
+ [Parameter(Mandatory = $false, Position = 4)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$WaitSeconds
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Send-ADTKeys]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('WaitSeconds'))
+ {
+ $PSBoundParameters.WaitDuration = [System.TimeSpan]::FromSeconds($WaitSeconds)
+ $null = $PSBoundParameters.Remove('WaitSeconds')
+ }
+ try
+ {
+ Send-ADTKeys @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTShortcut
+#
+#---------------------------------------------------------------------------
+
+function Get-Shortcut
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTShortcut]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Get-ADTShortcut @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTShortcut
+#
+#---------------------------------------------------------------------------
+
+function Set-Shortcut
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = 'Default')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = 'Pipeline')]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Hashtable]$PathHash,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TargetPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Arguments,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$IconLocation,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$IconIndex,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Description,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Normal', 'Maximized', 'Minimized', 'DontChange')]
+ [System.String]$WindowStyle,
+
+ [Parameter(Mandatory = $false)]
+ [System.Nullable[System.Boolean]]$RunAsAdmin,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Hotkey,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTShortcut]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ # Set up collector for piped in path objects.
+ $paths = [System.Collections.Specialized.StringCollection]::new()
+ }
+
+ process
+ {
+ # Add all paths to the collector.
+ if ($PSCmdlet.ParameterSetName.Equals('Default'))
+ {
+ $paths.Add($Path)
+ }
+ elseif ($PSCmdlet.ParameterSetName.Equals('Pipeline') -and $PathHash.ContainsKey('Path') -and ![System.String]::IsNullOrWhiteSpace($PathHash.Path))
+ {
+ $paths.Add($PathHash.Path)
+ }
+ }
+
+ end
+ {
+ # Process provided paths if we have any.
+ if ($paths.Count)
+ {
+ try
+ {
+ if ($PSBoundParameters.ContainsKey('Path'))
+ {
+ $null = $PSBoundParameters.Remove('Path')
+ }
+ $paths | Set-ADTShortcut @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around New-ADTShortcut
+#
+#---------------------------------------------------------------------------
+
+function New-Shortcut
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TargetPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Arguments,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$IconLocation,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$IconIndex,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Description,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Normal', 'Maximized', 'Minimized')]
+ [System.String]$WindowStyle,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$RunAsAdmin,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Hotkey,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [New-ADTShortcut]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ New-ADTShortcut @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Start-ADTProcessAsUser
+#
+#---------------------------------------------------------------------------
+
+function Execute-ProcessAsUser
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Justification = "Silenced to get the module build system going. This function is yet to be refactored.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$UserName = (Get-ADTRunAsActiveUser).NTAccount,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Path')]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TempPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Parameters')]
+ [System.String]$ArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [Alias('SecureParameters')]
+ [System.Management.Automation.SwitchParameter]$SecureArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('HighestAvailable', 'LeastPrivilege')]
+ [System.String]$RunLevel = 'HighestAvailable',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$Wait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Start-ADTProcessAsUser]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Announce dead parameters.
+ $null = ('TempPath', 'RunLevel').ForEach({
+ if ($PSBoundParameters.ContainsKey($_))
+ {
+ Write-ADTLogEntry -Message "The parameter '-$_' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ $PSBoundParameters.Remove($_)
+ }
+ })
+
+ # Translate the ContinueOnError state.
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ # Invoke underlying function.
+ try
+ {
+ if (($res = Start-ADTProcessAsUser @PSBoundParameters) -and $PassThru)
+ {
+ return $res.Result
+ }
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Close-ADTInstallationProgress
+#
+#---------------------------------------------------------------------------
+
+function Close-InstallationProgress
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateRange(1, 60)]
+ [System.Int32]$WaitingTime = 5
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Close-ADTInstallationProgress]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('WaitingTime'))
+ {
+ Write-ADTLogEntry -Message "The parameter '-WaitingTime' is discontinued and no longer has any effect." -Severity 2 -Source $MyInvocation.MyCommand.Name
+ }
+
+ # Invoke underlying function.
+ try
+ {
+ Close-ADTInstallationProgress
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around ConvertTo-ADTNTAccountOrSID
+#
+#---------------------------------------------------------------------------
+
+function ConvertTo-NTAccountOrSID
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'NTAccountToSID', ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$AccountName,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SIDToNTAccount', ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'WellKnownName', ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WellKnownSIDName,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'WellKnownName')]
+ [System.Management.Automation.SwitchParameter]$WellKnownToNTAccount
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [ConvertTo-ADTNTAccountOrSID]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Set up collector for pipelined input.
+ $pipedInput = [System.Collections.Specialized.StringCollection]::new()
+ }
+
+ process
+ {
+ # Only add non-null strings to our collector.
+ if (![System.String]::IsNullOrWhiteSpace(($thisInput = Get-Variable -Name $PSCmdlet.ParameterSetName -ValueOnly)))
+ {
+ $null = $pipedInput.Add($thisInput)
+ }
+ }
+
+ end
+ {
+ # Only proceed if we have collected input.
+ if (!$pipedInput.Count)
+ {
+ return
+ }
+
+ try
+ {
+ $null = $PSBoundParameters.Remove($PSCmdlet.ParameterSetName)
+ $pipedInput | ConvertTo-ADTNTAccountOrSID @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTDeferHistory
+#
+#---------------------------------------------------------------------------
+
+function Get-DeferHistory
+{
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTDeferHistory]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Invoke underlying function.
+ try
+ {
+ Get-ADTDeferHistory
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTDeferHistory
+#
+#---------------------------------------------------------------------------
+
+function Set-DeferHistory
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$DeferTimesRemaining,
+
+ [Parameter(Mandatory = $false)]
+ [AllowEmptyString()]
+ [System.String]$DeferDeadline
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTDeferHistory]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Invoke underlying function.
+ try
+ {
+ Set-ADTDeferHistory @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTMsiTableProperty
+#
+#---------------------------------------------------------------------------
+
+function Get-MsiTableProperty
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$TransformPath,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'TableInfo')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Table,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'TableInfo')]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$TablePropertyNameColumnNum,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'TableInfo')]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$TablePropertyValueColumnNum,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SummaryInfo')]
+ [System.Management.Automation.SwitchParameter]$GetSummaryInformation,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTMsiTableProperty]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Get-ADTMsiTableProperty @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Set-ADTMsiProperty
+#
+#---------------------------------------------------------------------------
+
+function Set-MsiProperty
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.__ComObject]$DataBase,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$PropertyName,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$PropertyValue,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Set-ADTMsiProperty]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+ try
+ {
+ Set-ADTMsiProperty @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTMsiExitCodeMessage
+#
+#---------------------------------------------------------------------------
+
+function Get-MsiExitCodeMessage
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$MsiExitCode
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTMsiExitCodeMessage]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Invoke underlying function.
+ try
+ {
+ Get-ADTMsiExitCodeMessage @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTObjectProperty
+#
+#---------------------------------------------------------------------------
+
+function Get-ObjectProperty
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$InputObject,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$PropertyName,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object[]]$ArgumentList
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTObjectProperty]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Invoke underlying function.
+ try
+ {
+ Get-ADTObjectProperty @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Invoke-ADTObjectMethod
+#
+#---------------------------------------------------------------------------
+
+function Invoke-ObjectMethod
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$InputObject,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$MethodName,
+
+ [Parameter(Mandatory = $false, Position = 2, ParameterSetName = 'Positional')]
+ [ValidateNotNullOrEmpty()]
+ [System.Object[]]$ArgumentList,
+
+ [Parameter(Mandatory = $true, Position = 2, ParameterSetName = 'Named')]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Hashtable]$Parameter
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Invoke-ADTObjectMethod]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Invoke underlying function.
+ try
+ {
+ Invoke-ADTObjectMethod @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Get-ADTPEFileArchitecture
+#
+#---------------------------------------------------------------------------
+
+function Get-PEFileArchitecture
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [ValidateScript({ Test-Path -LiteralPath $_ -PathType Leaf })]
+ [Systemn.IO.FileInfo[]]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [Systemn.Boolean]$ContinueOnError = $true,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTPEFileArchitecture]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ # Set up collector for pipelined input.
+ $filePaths = [System.Collections.Generic.List[System.IO.FileInfo]]::new()
+ }
+
+ process
+ {
+ # Collect all input for processing at the end.
+ if ($null -ne $FilePath)
+ {
+ $filePaths.Add($FilePath)
+ }
+ }
+
+ end
+ {
+ # Only process if we have files in our collector.
+ if (!$filePaths.Count)
+ {
+ return
+ }
+
+ try
+ {
+ if ($PSBoundParameters.ContainsKey('FilePath'))
+ {
+ $null = $PSBoundParameters.Remove('FilePath')
+ }
+ $filePaths | Get-ADTPEFileArchitecture @PSBoundParameters | & {
+ process
+ {
+ switch ([System.UInt16]$_)
+ {
+ 0
+ {
+ # The contents of this file are assumed to be applicable to any machine type
+ 'Native'
+ break
+ }
+ 0x014C
+ {
+ # File for Windows 32-bit systems
+ '32BIT'
+ break
+ }
+ 0x0200
+ {
+ # File for Intel Itanium x64 processor family
+ 'Itanium-x64'
+ break
+ }
+ 0x8664
+ {
+ # File for Windows 64-bit systems
+ '64BIT'
+ break
+ }
+ default
+ {
+ 'Unknown'
+ break
+ }
+ }
+
+ }
+ }
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around Test-ADTMutexAvailability
+#
+#---------------------------------------------------------------------------
+
+function Test-IsMutexAvailable
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateLength(1, 260)]
+ [System.String]$MutexName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$MutexWaitTime
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and any dead parameters before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Test-ADTMutexAvailability]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+
+ # Invoke underlying function.
+ try
+ {
+ Test-ADTMutexAvailability @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Wrapper around New-ADTZipFile
+#
+#---------------------------------------------------------------------------
+
+function New-ZipFile
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DestinationArchiveDirectoryPath,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DestinationArchiveFileName,
+
+ [Parameter(Mandatory = $true, Position = 2, ParameterSetName = 'SourceDirectoryPath')]
+ [ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })]
+ [System.String[]]$SourceDirectoryPath,
+
+ [Parameter(Mandatory = $true, Position = 2, ParameterSetName = 'SourceFilePath')]
+ [ValidateScript({ Test-Path -LiteralPath $_ -PathType Leaf })]
+ [System.String[]]$SourceFilePath,
+
+ [Parameter(Mandatory = $false, Position = 3)]
+ [System.Management.Automation.SwitchParameter]$RemoveSourceAfterArchiving,
+
+ [Parameter(Mandatory = $false, Position = 4)]
+ [System.Management.Automation.SwitchParameter]$OverWriteArchive,
+
+ [Parameter(Mandatory = $false, Position = 5)]
+ [ValidateNotNullOrEmpty()]
+ [System.Boolean]$ContinueOnError = $true
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce overall deprecation and translate $ContinueOnError to an ActionPreference before executing.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [New-ADTZipFile]. Please migrate your scripts to use the new function." -Severity 2 -DebugMessage:$noDepWarnings
+ if ($PSBoundParameters.ContainsKey('ContinueOnError'))
+ {
+ $null = $PSBoundParameters.Remove('ContinueOnError')
+ }
+ if (!$ContinueOnError)
+ {
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::Stop
+ }
+
+ # Convert source path parameter.
+ $PSBoundParameters.Add('LiteralPath', $PSBoundParameters.($PSCmdlet.ParameterSetName))
+ $null = $PSBoundParameters.Remove($PSCmdlet.ParameterSetName)
+
+ # Convert destination parameters.
+ $PSBoundParameters.Add('DestinationPath', [System.IO.Path]::Combine($DestinationArchiveDirectoryPath, $DestinationArchiveFileName))
+ $null = $PSBoundParameters.Remove('DestinationArchiveDirectoryPath')
+ $null = $PSBoundParameters.Remove('DestinationArchiveFileName')
+
+ # Convert $OverWriteArchive.
+ if ($PSBoundParameters.ContainsKey('OverWriteArchive'))
+ {
+ $PSBoundParameters.Add('Force', $OverWriteArchive)
+ $null = $PSBoundParameters.Remove('OverWriteArchive')
+ }
+
+ # Invoke replacement function.
+ try
+ {
+ New-ADTZipFile @PSBoundParameters
+ }
+ catch
+ {
+ if (!$ContinueOnError)
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Deprecation announcement for Set-PinnedApplication
+#
+#---------------------------------------------------------------------------
+
+function Set-PinnedApplication
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This compatibility wrapper function cannot support ShouldProcess for backwards compatiblity purposes.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Action', Justification = "The parameter is not used as the function is a deprecation announcement and performs no actions.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'FilePath', Justification = "The parameter is not used as the function is a deprecation announcement and performs no actions.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('PinToStartMenu', 'UnpinFromStartMenu', 'PinToTaskbar', 'UnpinFromTaskbar')]
+ [System.String]$Action,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FilePath
+ )
+
+ # Set strict mode to the highest within this function's scope.
+ Set-StrictMode -Version 3
+
+ # Announce that this function is no more and therefore does nothing within the deployment script.
+ Write-ADTLogEntry -Message "The function [$($MyInvocation.MyCommand.Name)] has been removed from PSAppDeployToolkit as its functionality no longer works with Windows 10 1809 or higher targets." -Severity 2
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Direct copy of Write-FunctionHeaderOrFooter for backwards compatibility reasons.
+#
+#---------------------------------------------------------------------------
+
+function Write-FunctionHeaderOrFooter
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$CmdletName,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Header')]
+ [AllowEmptyCollection()]
+ [System.Collections.Hashtable]$CmdletBoundParameters,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Header')]
+ [System.Management.Automation.SwitchParameter]$Header,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Footer')]
+ [System.Management.Automation.SwitchParameter]$Footer
+ )
+
+ if ($Header)
+ {
+ Write-ADTLogEntry -Message 'Function Start' -Source ${CmdletName} -DebugMessage
+
+ # Get the parameters that the calling function was invoked with.
+ if ([System.String]$CmdletBoundParameters = $CmdletBoundParameters | Format-Table -Property @{ Label = 'Parameter'; Expression = { "[-$($_.Key)]" } }, @{ Label = 'Value'; Expression = { $_.Value }; Alignment = 'Left' }, @{ Label = 'Type'; Expression = { $_.Value.GetType().Name }; Alignment = 'Left' } -AutoSize -Wrap | Out-String)
+ {
+ Write-ADTLogEntry -Message "Function invoked with bound parameter(s): `r`n$CmdletBoundParameters" -Source ${CmdletName} -DebugMessage
+ }
+ else
+ {
+ Write-ADTLogEntry -Message 'Function invoked without any bound parameters.' -Source ${CmdletName} -DebugMessage
+ }
+ }
+ elseif ($Footer)
+ {
+ Write-ADTLogEntry -Message 'Function End' -Source ${CmdletName} -DebugMessage
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Module and session code
+#
+#---------------------------------------------------------------------------
+
+# Set required variables to ensure module functionality.
+$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
+$ProgressPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
+Set-StrictMode -Version 3
+
+# Import our module backend.
+$moduleName = if ([System.IO.Directory]::Exists("$PSScriptRoot\PSAppDeployToolkit"))
+{
+ Get-ChildItem -LiteralPath $PSScriptRoot\PSAppDeployToolkit -Recurse -File | Unblock-File
+ "$PSScriptRoot\PSAppDeployToolkit\PSAppDeployToolkit.psd1"
+}
+elseif ([System.IO.Directory]::Exists("$PSScriptRoot\..\..\..\..\PSAppDeployToolkit"))
+{
+ Get-ChildItem -LiteralPath $PSScriptRoot\..\..\..\..\PSAppDeployToolkit -Recurse -File | Unblock-File
+ "$PSScriptRoot\..\..\..\..\PSAppDeployToolkit\PSAppDeployToolkit.psd1"
+}
+else
+{
+ 'PSAppDeployToolkit'
+}
+Remove-Module -Name PSAppDeployToolkit* -Force
+$adtModule = Import-Module -FullyQualifiedName @{ ModuleName = $moduleName; Guid = '8c3c366b-8606-4576-9f2d-4051144f7ca2'; ModuleVersion = '4.0.6' } -Force -PassThru -ErrorAction Stop
+
+# Get all parameters from Open-ADTSession that are considered frontend params/variables.
+$sessionVars = $adtModule.ExportedCommands.'Open-ADTSession'.Parameters.Values | & {
+ process
+ {
+ if ($_.ParameterSets.Values.HelpMessage -match '^Frontend (Parameter|Variable)$')
+ {
+ return $_.Name
+ }
+ }
+}
+
+# Build out parameter hashtable and open a new deployment session.
+$sessionParams = Get-Variable -Name $sessionVars -ErrorAction Ignore | & {
+ begin
+ {
+ # Open collector to hold valid parameters.
+ $sessionParams = @{}
+ }
+
+ process
+ {
+ # Add the parameter if it's not null.
+ if (![System.String]::IsNullOrWhiteSpace((Out-String -InputObject $_.Value)))
+ {
+ $sessionParams.Add($_.Name, $_.Value)
+ }
+ }
+
+ end
+ {
+ # Remove AppScriptDate if it's Deploy-Application.ps1's default value.
+ if ($sessionParams.ContainsKey('AppScriptDate') -and ($sessionParams.AppScriptDate -eq 'XX/XX/20XX'))
+ {
+ $null = $sessionParams.Remove('AppScriptDate')
+ }
+
+ # Redefine DeployAppScriptParameters due bad casting in Deploy-Application.ps1.
+ if ($sessionParams.ContainsKey('DeployAppScriptParameters'))
+ {
+ $sessionParams.DeployAppScriptParameters = (Get-PSCallStack)[1].InvocationInfo.BoundParameters
+ }
+
+ # Return the dictionary to the caller.
+ return $sessionParams
+ }
+}
+Open-ADTSession -SessionState $ExecutionContext.SessionState @sessionParams
+
+# Define aliases for some functions to maintain backwards compatibility.
+New-Alias -Name Refresh-SessionEnvironmentVariables -Value Update-ADTEnvironmentPsProvider -Option ReadOnly -Force
+New-Alias -Name Refresh-Desktop -Value Update-Desktop -Option ReadOnly -Force
+
+# Finalize setup of AppDeployToolkitMain.ps1.
+Set-Item -LiteralPath $adtWrapperFuncs -Options ReadOnly
+New-Variable -Name noDepWarnings -Value (($adtConfig = Get-ADTConfig).Toolkit.ContainsKey('WrapperWarnings') -and !$adtConfig.Toolkit.WrapperWarnings) -Option ReadOnly -Force
+Remove-Variable -Name adtConfig, adtModule, adtWrapperFuncs, sessionParams, sessionVars -Force -Confirm:$false
+Set-StrictMode -Version 1
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Compatibility extension support
+#
+#---------------------------------------------------------------------------
+
+if ((Test-Path -LiteralPath "$PSScriptRoot\AppDeployToolkitExtensions.ps1" -PathType Leaf))
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'scriptParentPath', Justification = "This variable is used within a dot-sourced script that PSScriptAnalyzer has no visibility of.")]
+ $scriptParentPath = if ($invokingScript = (Get-Variable -Name 'MyInvocation').Value.ScriptName)
+ {
+ # If this script was invoked by another script.
+ Split-Path -Path $invokingScript -Parent
+ }
+ else
+ {
+ # If this script was not invoked by another script, fall back to the directory one level above this script.
+ (Get-Item -LiteralPath (Split-Path -Path $MyInvocation.MyCommand.Definition -Parent)).Parent.FullName
+ }
+ . "$PSScriptRoot\AppDeployToolkitExtensions.ps1"
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDnarrb2MryShTz
+# 2rhUxGlQzsR0KTP1uMRd/CjhJtS1v6CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQginQOBLiMe12fJu1Tjx7nsmiTRyJUeP1U/y8e79j+RVww
+# DQYJKoZIhvcNAQEBBQAEggGAllJeowvKPojGvk8FeLxAcbOInh8HhVS5JqKmO/LW
+# BS71BvcpclqY0I383t1+4cjGyY9p9e6ECAAD0Skio0xiCSLJTx+S7P3vgBouTtYb
+# zHLzZZBrBroLQt73Kzt2y+zVtgyjSe/DihTSLLTuRyhgatlD7Cyf+/LnhSjy+PgZ
+# BrfWOOobeAC5aE4Qb1n7PMT3Dgj0tD/mbJthXm+4IdGFGvlEihIYr47fS9tiYM+D
+# j6uvAZJEZ1nyQI9jMshNNBGd/dxH0wQxRb64D+C+XkW7l3Kzmz7PuFns9EChk8bc
+# cW4chWDlZkIN0MYyNBe9f0FoIWkfElIMX9du7l2fHWnnLhGkhqHKbzh5q0chJDsc
+# cIr8UnngdGHy9oM4wg3R8pEMeNt2+TGT8cBQpmReZxzuh7ZsJvENaas2li3hcNGr
+# S7C3FoJO2+hP2LVFc06U83OkjAv+V89zxTsesv8K2Bsh6Mksze/E42ZkF5//F6kG
+# 7OyDR/ZkwEJmq+r2zdbq4AhXoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIOnk3QgW
+# v6+a0LMqNrj+X4R2kdwL4Xf/KhaxFEqEjuXzAhEA5tvTQ3Mj3UasW4EVw/k4hhgP
+# MjAyNTAyMjMyMzQ2NTVaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ2NTVaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCBbir60ahsXZaPFa0zrHYuKcMDi9BNN1ik6JDa/
+# XdQI5DA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgACx1NM7E4nCfVWQgC9Rexz
+# iHpqnUs+WdGjZoSeaTJf2siqdNIDjeasOdvbc8Wm24LDSfSmVFykbXqkcesxgMrx
+# cP7sWM15R3jTFSPrbZHGOU14PTWaElQgJzV4+onG26bQdb+7N3y+mHo9ppzZm4W+
+# B6J38Ns8XoXneok3kfxwADjfYpm1eGKEqrkdQ/gh0JpO16auMmo/PUGjsg4r4YLG
+# KzRgWK/iRndiHDk8P1OnQmEb0RXH3/v7qxuLvbVRLSMiT+nfa00D2TQM1sYp649H
+# NxFpzR5Lwpku6QExJG5FrUHtOdgw4fvu+I0xjmlTYwuFO4VQMbnf9PuHsnX5uCH6
+# rO6rYZniItBQsn1f3EvFxhK6qUMQNmFRLlwGfTC1OH87zjslKwPMQsAbgPeT2G7q
+# j4AIleCkvnSN/oMK6z7mQ4yYAiD5Pk8K6PhkcbK73jJAja48Yo03Vm8QjwL1X6tV
+# 5rruS7cLjMrsORvm9IYteKKQ1+27EU4mDCLD3oyJTNzCUjGHZpClZXqpYKshVYXd
+# QOIEIt3w4+yamarFVMaaOMN9dTqI9GxG1r5Knwe+zKJ9y39f5Pk2w/pJFKT/Ma6+
+# x7o7NREc9N2bv0VykGZpQP1huA7Gxh+N4dytpOqu7a0YyErgabrfBJHj1KY505pJ
+# RTTRAXCAcdgOkOUG6XNFYg==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/COPYING.Lesser b/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/COPYING.Lesser
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v3/AppDeployToolkit/COPYING.Lesser
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/PSAppDeplyToolkit/Frontend/v3/COPYING.Lesser b/PSAppDeplyToolkit/Frontend/v3/COPYING.Lesser
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v3/COPYING.Lesser
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/PSAppDeplyToolkit/Frontend/v3/Deploy-Application.exe b/PSAppDeplyToolkit/Frontend/v3/Deploy-Application.exe
new file mode 100644
index 0000000..bc73ea6
Binary files /dev/null and b/PSAppDeplyToolkit/Frontend/v3/Deploy-Application.exe differ
diff --git a/PSAppDeplyToolkit/Frontend/v4/COPYING.Lesser b/PSAppDeplyToolkit/Frontend/v4/COPYING.Lesser
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v4/COPYING.Lesser
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/PSAppDeplyToolkit/Frontend/v4/Invoke-AppDeployToolkit.exe b/PSAppDeplyToolkit/Frontend/v4/Invoke-AppDeployToolkit.exe
new file mode 100644
index 0000000..b59e8de
Binary files /dev/null and b/PSAppDeplyToolkit/Frontend/v4/Invoke-AppDeployToolkit.exe differ
diff --git a/PSAppDeplyToolkit/Frontend/v4/Invoke-AppDeployToolkit.ps1 b/PSAppDeplyToolkit/Frontend/v4/Invoke-AppDeployToolkit.ps1
new file mode 100644
index 0000000..c49f30d
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v4/Invoke-AppDeployToolkit.ps1
@@ -0,0 +1,574 @@
+<#
+
+.SYNOPSIS
+PSAppDeployToolkit - This script performs the installation or uninstallation of an application(s).
+
+.DESCRIPTION
+- The script is provided as a template to perform an install, uninstall, or repair of an application(s).
+- The script either performs an "Install", "Uninstall", or "Repair" deployment type.
+- The install deployment type is broken down into 3 main sections/phases: Pre-Install, Install, and Post-Install.
+
+The script imports the PSAppDeployToolkit module which contains the logic and functions required to install or uninstall an application.
+
+PSAppDeployToolkit is licensed under the GNU LGPLv3 License - (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation, either version 3 of the License, or 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 Lesser General Public License along with this program. If not, see .
+
+.PARAMETER DeploymentType
+The type of deployment to perform.
+
+.PARAMETER DeployMode
+Specifies whether the installation should be run in Interactive (shows dialogs), Silent (no dialogs), or NonInteractive (dialogs without prompts) mode.
+
+NonInteractive mode is automatically set if it is detected that the process is not user interactive.
+
+.PARAMETER AllowRebootPassThru
+Allows the 3010 return code (requires restart) to be passed back to the parent process (e.g. SCCM) if detected from an installation. If 3010 is passed back to SCCM, a reboot prompt will be triggered.
+
+.PARAMETER TerminalServerMode
+Changes to "user install mode" and back to "user execute mode" for installing/uninstalling applications for Remote Desktop Session Hosts/Citrix servers.
+
+.PARAMETER DisableLogging
+Disables logging to file for the script.
+
+.EXAMPLE
+powershell.exe -File Invoke-AppDeployToolkit.ps1 -DeployMode Silent
+
+.EXAMPLE
+powershell.exe -File Invoke-AppDeployToolkit.ps1 -AllowRebootPassThru
+
+.EXAMPLE
+powershell.exe -File Invoke-AppDeployToolkit.ps1 -DeploymentType Uninstall
+
+.EXAMPLE
+Invoke-AppDeployToolkit.exe -DeploymentType "Install" -DeployMode "Silent"
+
+.INPUTS
+None. You cannot pipe objects to this script.
+
+.OUTPUTS
+None. This script does not generate any output.
+
+.NOTES
+Toolkit Exit Code Ranges:
+- 60000 - 68999: Reserved for built-in exit codes in Invoke-AppDeployToolkit.ps1, and Invoke-AppDeployToolkit.exe
+- 69000 - 69999: Recommended for user customized exit codes in Invoke-AppDeployToolkit.ps1
+- 70000 - 79999: Recommended for user customized exit codes in PSAppDeployToolkit.Extensions module.
+
+.LINK
+https://psappdeploytoolkit.com
+
+#>
+
+[CmdletBinding()]
+param
+(
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Install', 'Uninstall', 'Repair')]
+ [PSDefaultValue(Help = 'Install', Value = 'Install')]
+ [System.String]$DeploymentType,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Interactive', 'Silent', 'NonInteractive')]
+ [PSDefaultValue(Help = 'Interactive', Value = 'Interactive')]
+ [System.String]$DeployMode,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$AllowRebootPassThru,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$TerminalServerMode,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DisableLogging
+)
+
+
+##================================================
+## MARK: Variables
+##================================================
+
+$adtSession = @{
+ # App variables.
+ AppVendor = ''
+ AppName = ''
+ AppVersion = ''
+ AppArch = ''
+ AppLang = 'EN'
+ AppRevision = '01'
+ AppSuccessExitCodes = @(0)
+ AppRebootExitCodes = @(1641, 3010)
+ AppScriptVersion = '1.0.0'
+ AppScriptDate = '2000-12-31'
+ AppScriptAuthor = ''
+
+ # Install Titles (Only set here to override defaults set by the toolkit).
+ InstallName = ''
+ InstallTitle = ''
+
+ # Script variables.
+ DeployAppScriptFriendlyName = $MyInvocation.MyCommand.Name
+ DeployAppScriptVersion = '4.0.6'
+ DeployAppScriptParameters = $PSBoundParameters
+}
+
+function Install-ADTDeployment
+{
+ ##================================================
+ ## MARK: Pre-Install
+ ##================================================
+ $adtSession.InstallPhase = "Pre-$($adtSession.DeploymentType)"
+
+ ## Show Welcome Message, close Internet Explorer if required, allow up to 3 deferrals, verify there is enough disk space to complete the install, and persist the prompt.
+ Show-ADTInstallationWelcome -CloseProcesses iexplore -AllowDefer -DeferTimes 3 -CheckDiskSpace -PersistPrompt
+
+ ## Show Progress Message (with the default message).
+ Show-ADTInstallationProgress
+
+ ##
+
+
+ ##================================================
+ ## MARK: Install
+ ##================================================
+ $adtSession.InstallPhase = $adtSession.DeploymentType
+
+ ## Handle Zero-Config MSI installations.
+ if ($adtSession.UseDefaultMsi)
+ {
+ $ExecuteDefaultMSISplat = @{ Action = $adtSession.DeploymentType; FilePath = $adtSession.DefaultMsiFile }
+ if ($adtSession.DefaultMstFile)
+ {
+ $ExecuteDefaultMSISplat.Add('Transform', $adtSession.DefaultMstFile)
+ }
+ Start-ADTMsiProcess @ExecuteDefaultMSISplat
+ if ($adtSession.DefaultMspFiles)
+ {
+ $adtSession.DefaultMspFiles | Start-ADTMsiProcess -Action Patch
+ }
+ }
+
+ ##
+
+
+ ##================================================
+ ## MARK: Post-Install
+ ##================================================
+ $adtSession.InstallPhase = "Post-$($adtSession.DeploymentType)"
+
+ ##
+
+
+ ## Display a message at the end of the install.
+ if (!$adtSession.UseDefaultMsi)
+ {
+ Show-ADTInstallationPrompt -Message 'You can customize text to appear at the end of an install or remove it completely for unattended installations.' -ButtonRightText 'OK' -Icon Information -NoWait
+ }
+}
+
+function Uninstall-ADTDeployment
+{
+ ##================================================
+ ## MARK: Pre-Uninstall
+ ##================================================
+ $adtSession.InstallPhase = "Pre-$($adtSession.DeploymentType)"
+
+ ## Show Welcome Message, close Internet Explorer with a 60 second countdown before automatically closing.
+ Show-ADTInstallationWelcome -CloseProcesses iexplore -CloseProcessesCountdown 60
+
+ ## Show Progress Message (with the default message).
+ Show-ADTInstallationProgress
+
+ ##
+
+
+ ##================================================
+ ## MARK: Uninstall
+ ##================================================
+ $adtSession.InstallPhase = $adtSession.DeploymentType
+
+ ## Handle Zero-Config MSI uninstallations.
+ if ($adtSession.UseDefaultMsi)
+ {
+ $ExecuteDefaultMSISplat = @{ Action = $adtSession.DeploymentType; FilePath = $adtSession.DefaultMsiFile }
+ if ($adtSession.DefaultMstFile)
+ {
+ $ExecuteDefaultMSISplat.Add('Transform', $adtSession.DefaultMstFile)
+ }
+ Start-ADTMsiProcess @ExecuteDefaultMSISplat
+ }
+
+ ##
+
+
+ ##================================================
+ ## MARK: Post-Uninstallation
+ ##================================================
+ $adtSession.InstallPhase = "Post-$($adtSession.DeploymentType)"
+
+ ##
+}
+
+function Repair-ADTDeployment
+{
+ ##================================================
+ ## MARK: Pre-Repair
+ ##================================================
+ $adtSession.InstallPhase = "Pre-$($adtSession.DeploymentType)"
+
+ ## Show Welcome Message, close Internet Explorer with a 60 second countdown before automatically closing.
+ Show-ADTInstallationWelcome -CloseProcesses iexplore -CloseProcessesCountdown 60
+
+ ## Show Progress Message (with the default message).
+ Show-ADTInstallationProgress
+
+ ##
+
+
+ ##================================================
+ ## MARK: Repair
+ ##================================================
+ $adtSession.InstallPhase = $adtSession.DeploymentType
+
+ ## Handle Zero-Config MSI repairs.
+ if ($adtSession.UseDefaultMsi)
+ {
+ $ExecuteDefaultMSISplat = @{ Action = $adtSession.DeploymentType; FilePath = $adtSession.DefaultMsiFile }
+ if ($adtSession.DefaultMstFile)
+ {
+ $ExecuteDefaultMSISplat.Add('Transform', $adtSession.DefaultMstFile)
+ }
+ Start-ADTMsiProcess @ExecuteDefaultMSISplat
+ }
+
+ ##
+
+
+ ##================================================
+ ## MARK: Post-Repair
+ ##================================================
+ $adtSession.InstallPhase = "Post-$($adtSession.DeploymentType)"
+
+ ##
+}
+
+
+##================================================
+## MARK: Initialization
+##================================================
+
+# Set strict error handling across entire operation.
+$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
+$ProgressPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
+Set-StrictMode -Version 1
+
+# Import the module and instantiate a new session.
+try
+{
+ $moduleName = if ([System.IO.File]::Exists("$PSScriptRoot\..\..\..\PSAppDeployToolkit\PSAppDeployToolkit.psd1"))
+ {
+ Get-ChildItem -LiteralPath $PSScriptRoot\..\..\..\PSAppDeployToolkit -Recurse -File | Unblock-File -ErrorAction Ignore
+ "$PSScriptRoot\..\..\..\PSAppDeployToolkit\PSAppDeployToolkit.psd1"
+ }
+ else
+ {
+ 'PSAppDeployToolkit'
+ }
+ Import-Module -FullyQualifiedName @{ ModuleName = $moduleName; Guid = '8c3c366b-8606-4576-9f2d-4051144f7ca2'; ModuleVersion = '4.0.6' } -Force
+ try
+ {
+ $iadtParams = Get-ADTBoundParametersAndDefaultValues -Invocation $MyInvocation
+ $adtSession = Open-ADTSession -SessionState $ExecutionContext.SessionState @adtSession @iadtParams -PassThru
+ }
+ catch
+ {
+ Remove-Module -Name PSAppDeployToolkit* -Force
+ throw
+ }
+}
+catch
+{
+ $Host.UI.WriteErrorLine((Out-String -InputObject $_ -Width ([System.Int32]::MaxValue)))
+ exit 60008
+}
+
+
+##================================================
+## MARK: Invocation
+##================================================
+
+try
+{
+ Get-Item -Path $PSScriptRoot\PSAppDeployToolkit.* | & {
+ process
+ {
+ Get-ChildItem -LiteralPath $_.FullName -Recurse -File | Unblock-File -ErrorAction Ignore
+ Import-Module -Name $_.FullName -Force
+ }
+ }
+ & "$($adtSession.DeploymentType)-ADTDeployment"
+ Close-ADTSession
+}
+catch
+{
+ Write-ADTLogEntry -Message ($mainErrorMessage = Resolve-ADTErrorRecord -ErrorRecord $_) -Severity 3
+ Show-ADTDialogBox -Text $mainErrorMessage -Icon Stop | Out-Null
+ Close-ADTSession -ExitCode 60001
+}
+finally
+{
+ Remove-Module -Name PSAppDeployToolkit* -Force
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB6pC+7Z3l38I5l
+# WdH+S31QMsrsRh97bWyOxvfoGTdv5KCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQghXnjYSuAMhhTAaBjyWBcVtM4iUSCiC0ApclbXXpRz2gw
+# DQYJKoZIhvcNAQEBBQAEggGAXURx2l5I57otcwnPYf5TjxX5PMLehIK2/1RzDJWE
+# yBJLu2OixMxOYrgq3vuvZ0Twz7Si/6TMWqWHrNaEv/x2KZnE1Vi4PdkqYeopOD09
+# 4JYEtZuiazw8LS95IgPgKQO+N2E0hIrqO2qdsjs9ph1xAnMcVwj/DLv9RihzDmV0
+# dXlqpri55ePlAoq3wfAH6mlGy1PV5dX9FF52MKaAa6v5WLjLOkLNUumJHMLEyFhD
+# FHPwryb3MVsEJw5Ze+OaifpPTyuCvSWd96OdnYptNd0wGeC3Tm18nqU7eP/aMPsY
+# drzk7US/LYCgCwXjF7N/4b4pbK30IJ3KFIGsoLLw5vw+7XubMH6Hh7FIQOueyF7E
+# 8zws0VzuGs0BniPdulqWW9itY7pdGxMtDmi3D5KnDOL7njXZyfBkxOvppACdgQZB
+# yFcRWsxmy7ErdC0Cq2QtYhOajAHEEC0Li6pdBmoYk4oMk1DtCntuwvm6K07TbqJj
+# aPDNPtwxWEYFRMG/dkbvf/yBoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIGij8Rkx
+# Sct8sPa25Qd2wibFSvC9o4fM7511DTgwffx9AhEA6wIJ6Jlrp43bMzfK56Z3CBgP
+# MjAyNTAyMjMyMzQ3MDlaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3MDlaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCCUSyqpbQdl4mae8mwryJ/KyDjAIS00H2vPiVyF
+# d6uw7DA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgAaIfU59EW9eV0DtUECDP5+
+# XaU/kN+RWN6b7f1ozGszhi5/53wJJmIdqJgSLCOIUBRvR4Oe3pcZh/rgBmBjqCcp
+# EZWxzbpnixhkS8/raRk6LTEBpQ1z7HKv0Lqmf8ydGimDdPbhvWuJXVH0PKDdAUuP
+# znbNETuZWlEiVtcbEB7Ufwy7XqZtXtG7Dtf7XbU0jN/OPCl/tEVQjeanvoOT4+Y9
+# Ldhje2iNc8cLevVJVpV18RQDzLQe8yWTGe4uE4S0TNSMexrjgWbdSayboyQxRq9r
+# q6EdUQtmDYsNT7LFeU04dC6jTd5nudgs+74BVSFpvJFGG9ay2G0sunMGJpAZqoAR
+# 4vMesvvgRXoNo24zH72e+UkHYRSC8FvKjjcG1D9w+Qalr2MuDvcu4VzUWu6PzXo+
+# lS+ziOolV484ktn3bcyISkztU9SPokq9RM/q/4YLMrcc1CcBqEtin1B0OqMRk2gy
+# 24xrQl7P8q1f+FQY63RJdRlJ2P59w2T0vK6QR6RG9hdw2B84EHVhScJ9OinTdVTD
+# XXEMN5n3PtSG2Lu9SvmUHNKUjnYuuv421jy5vOC54Vfsogs1/ym3pVydiuY6meFH
+# M9XDrRvjR1am34n70HiG1EscbImowzIn4xj3JdpFl/Y9ohrrC3rtO50LuTm0BDcn
+# +zoMEhpT0OaZC86vrGY1ng==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/COPYING.Lesser b/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/COPYING.Lesser
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/COPYING.Lesser
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/PSAppDeployToolkit.Extensions.psd1 b/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/PSAppDeployToolkit.Extensions.psd1
new file mode 100644
index 0000000..cd456a5
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/PSAppDeployToolkit.Extensions.psd1
@@ -0,0 +1,371 @@
+#
+# Module manifest for module 'PSAppDeployToolkit.Extensions'
+#
+# Generated on: 31/10/2024
+#
+
+@{
+ # Script module or binary module file associated with this manifest.
+ RootModule = 'PSAppDeployToolkit.Extensions.psm1'
+
+ # Version number of this module.
+ ModuleVersion = '4.0.6'
+
+ # Supported PSEditions
+ # CompatiblePSEditions = @()
+
+ # ID used to uniquely identify this module
+ GUID = '55276a4c-9fbb-49a4-8481-159113757c39'
+
+ # Author of this module
+ # Author = ''
+
+ # Company or vendor of this module
+ # CompanyName = ''
+
+ # Copyright statement for this module
+ # Copyright = ''
+
+ # Description of the functionality provided by this module
+ Description = 'Local extensions for PSAppDeployToolkit.'
+
+ # Minimum version of the Windows PowerShell engine required by this module
+ PowerShellVersion = '5.1.14393.0'
+
+ # Name of the Windows PowerShell host required by this module
+ # PowerShellHostName = ''
+
+ # Minimum version of the Windows PowerShell host required by this module
+ # PowerShellHostVersion = ''
+
+ # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
+ DotNetFrameworkVersion = '4.6.2.0'
+
+ # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
+ CLRVersion = '4.0.30319.42000'
+
+ # Processor architecture (None, X86, Amd64) required by this module
+ # ProcessorArchitecture = ''
+
+ # Modules that must be imported into the global environment prior to importing this module
+ RequiredModules = @(
+ @{ ModuleName = 'PSAppDeployToolkit'; GUID = '8c3c366b-8606-4576-9f2d-4051144f7ca2'; ModuleVersion = '4.0.6' }
+ )
+
+ # Assemblies that must be loaded prior to importing this module
+ # RequiredAssemblies = @()
+
+ # Script files (.ps1) that are run in the caller's environment prior to importing this module.
+ # ScriptsToProcess = @()
+
+ # Type files (.ps1xml) to be loaded when importing this module
+ # TypesToProcess = @()
+
+ # Format files (.ps1xml) to be loaded when importing this module
+ # FormatsToProcess = @()
+
+ # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
+ # NestedModules = @()
+
+ # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
+ FunctionsToExport = '*'
+
+ # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
+ # CmdletsToExport = ''
+
+ # Variables to export from this module
+ # VariablesToExport = ''
+
+ # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
+ # AliasesToExport = ''
+
+ # DSC resources to export from this module
+ # DscResourcesToExport = @()
+
+ # List of all modules packaged with this module
+ # ModuleList = @()
+
+ # List of all files packaged with this module
+ # FileList = @()
+
+ # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+ PrivateData = @{
+
+ PSData = @{
+
+ # Tags applied to this module. These help with module discovery in online galleries.
+ # Tags = @()
+
+ # A URL to the license for this module.
+ # LicenseUri = ''
+
+ # A URL to the main website for this project.
+ # ProjectUri = ''
+
+ # A URL to an icon representing this module.
+ # IconUri = ''
+
+ # ReleaseNotes of this module
+ # ReleaseNotes = ''
+
+ } # End of PSData hashtable
+
+ } # End of PrivateData hashtable
+
+ # HelpInfo URI of this module
+ # HelpInfoURI = ''
+
+ # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+ # DefaultCommandPrefix = ''
+}
+
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDvIxMitONzDcyr
+# Clq934Aw3YYlrt4mhBBTBzBx5WR8NqCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgA9TKPcfupd2cN1PT+cP1NiSYSszefHdcuNsmqXE1vE8w
+# DQYJKoZIhvcNAQEBBQAEggGAD9PpIuc9+R4lMX1cJuKfSt8TtOQ5/14L86/6XHV8
+# vtH5btji99FGX6MIIJiN/4JQjPGi8M0pNCkBBZ3IFoOWk8dHRKF7Bw7DxKWrDmB4
+# BwP9GV83LAv995XDu7GOUkny7a10V0BqffM/MZpyTd1zQOuTz+zFXvo5AS+3VNNS
+# 8zzPwAX4JLj/gvr5MwRDxRCE3JBLzk7S7lrcX76Crecsyl1+bdVT2PHO8+RYnXNJ
+# 4y3wKK6DcVNa+vGiDv5C0YTFvQCSonKknZHRs03XXqwOMekTqintnwgZNTnckuy1
+# FoPWzueR6z9dhXEYTaLAAgzJGIk5UfvcYu+X2+/xQ55SP1Dmj9WUZh/ETXoBGa13
+# kjKXE92gv+uwEDfhyEdscbM0kmGAxnqcYql6cOXgd4v0XeFyD0MtUNVT17fzsNQm
+# hjcqJ+bsh07IGAkkxHWe6prCuM/yi+3YY3bCByyuMQuZs6xRaW8vq4hiBES+icDR
+# 1bE45BvYMtBj8p0OMAklZFrGoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEILAtPsYE
+# Xol1lRgADfFv6npYlV/Pd7Jg9+HiGC6biGVqAhBjANtZU9UIIl9vnGWLj4pTGA8y
+# MDI1MDIyMzIzNDcwMVqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDcwMVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIPXON6vGTIT2qY2k31vSmEfQOGxTpRX5jznBn0HI
+# 9juSMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAEMWGBgfFzuHYDUIzbCVynn6
+# uSARMLVcwe/vNBlf0J1zTJWjBjjE8+WCUJeqR3sSHXxrmT2GAjLRbE0jOxMOsbB4
+# 4AY06OyN+zRCkenITtd0fKQo6exdt0O2DXcKuycqvahQSxLPdTPnJfUgSGASMYTw
+# hrTzJvLG1YV7U+cQ0iNaRnyy3oibmqSsy7VfxuM6wogaOXQgaXShRi449fV9RqUz
+# GNtnltmm7b5JoEp43gcnJfsG5xIULafe0Z5sMXqrc7Zb8a/phBqGgn1IeZjQQM1u
+# ADljcpYEPx30ze9mthn3FKh2TjeJ+A6hCI0QbpMr68XZ/+oT4yO1Qp5XZM7FqcTG
+# lnyDPxvgi1/nHx6KDoNZPsUmZ9nwYmRHrqtBJzxXHuGeM+PeVX/6AXu8VB5zCs6u
+# an5J1KUkfHpQzRLCeT3056QFW3qQFe5UoLQJVE4o1aI8z7S/yxSz6Elo6+HfdJr7
+# HugHMwrTbK/c5eoDAZHcfcwZbbPF3XF1qGBq6DM+A+9p1ZtyMExQ+34EwMJcX6aV
+# GyPrxGAoDw9+8GYZaUYMtRcKq2hc/UxqFPuQKD0KXTLimJNSVL+PQs2AcQXthC8S
+# tRnjl6XnxTqtpdpqr0YKZL6jmtKzBDzqTvPWYGxEo7yaVfC6/Yex1okDuPHlhc+0
+# TaPo35F1QzZfaw03pUuM
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/PSAppDeployToolkit.Extensions.psm1 b/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/PSAppDeployToolkit.Extensions.psm1
new file mode 100644
index 0000000..a370a6a
--- /dev/null
+++ b/PSAppDeplyToolkit/Frontend/v4/PSAppDeployToolkit.Extensions/PSAppDeployToolkit.Extensions.psm1
@@ -0,0 +1,356 @@
+<#
+
+.SYNOPSIS
+PSAppDeployToolkit.Extensions - Provides the ability to extend and customize the toolkit by adding your own functions that can be re-used.
+
+.DESCRIPTION
+This module is a template that allows you to extend the toolkit with your own custom functions.
+
+This module is imported by the Invoke-AppDeployToolkit.ps1 script which is used when installing or uninstalling an application.
+
+PSAppDeployToolkit is licensed under the GNU LGPLv3 License - (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation, either version 3 of the License, or 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 Lesser General Public License along with this program. If not, see .
+
+.LINK
+https://psappdeploytoolkit.com
+
+#>
+
+##*===============================================
+##* MARK: MODULE GLOBAL SETUP
+##*===============================================
+
+# Set strict error handling across entire module.
+$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
+$ProgressPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
+Set-StrictMode -Version 1
+
+
+##*===============================================
+##* MARK: FUNCTION LISTINGS
+##*===============================================
+
+function New-ADTExampleFunction
+{
+ <#
+ .SYNOPSIS
+ Basis for a new PSAppDeployToolkit extension function.
+
+ .DESCRIPTION
+ This function serves as the basis for a new PSAppDeployToolkit extension function.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ New-ADTExampleFunction
+
+ Invokes the New-ADTExampleFunction function and returns any output.
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ # Initialize function.
+ Initialize-ADTFunction -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ Write-Error -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ Invoke-ADTFunctionErrorHandler -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ Complete-ADTFunction -Cmdlet $PSCmdlet
+ }
+}
+
+
+##*===============================================
+##* MARK: SCRIPT BODY
+##*===============================================
+
+# Announce successful importation of module.
+Write-ADTLogEntry -Message "Module [$($MyInvocation.MyCommand.ScriptBlock.Module.Name)] imported successfully." -ScriptSection Initialization
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBgLDz8PBLi0P+3
+# 1iDsSFvA16kHIMow9VoDkyZGyiKbfaCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgynDoH1zYsdGOKFTjEBkqcK5/bK+8yNh4dOOwG/a7Sbsw
+# DQYJKoZIhvcNAQEBBQAEggGAPgsY3fuCOf5y60GFwgC68PESynAt6lwchrvxKBf9
+# fbGdKyT0vytk4OiQX0W/oHFG9eTnfESERCgra7IBvHpXuwPlw+XXSr7/+cxgHQG7
+# Q8nq144Svtu4nDZvk39dL9jsgiaJtrfpOCp3Jr7HE1rICrWWdaKKxnCCTekIy/N5
+# oQ2l/Ol5cake7Jks4ymPZo8J730Kebh1lixkRrGhcZIPc+IhB5oM3MFwgBca12lS
+# jxTp284VPixVvV99v7I+t8+7vGl8hEN/O46N4Mq5VrKSUIdE9oMYs817UU//fh22
+# OVlRUWkGWHbItdNxVGZjbym1NOQF4L8++65D6HC/k41kc2P/IydJrjGqtD7achkp
+# hfv5NUc4+j52jQUgiBCPwiOyWCmtBSXepp2kENkUmMIIwzAR3XZtYZwRvm6KekPS
+# wy3dUA1Re4y0zokQ3VM/GAs1GBsxSJK0cLNZ/nIhYySJo36c4FyqDMUMbvGNqnFS
+# WTDF/19GCUch21JmDCZTrivioYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIM3BZT57
+# FHXemPni19sT/yCSYD2xaymG47EsoenAU6mfAhBqwuoEpDierAej7UgKugOsGA8y
+# MDI1MDIyMzIzNDcwM1qgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDcwM1owKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIKzdxH71XTakx/Qn8UXlrQItp5YZVFWKGTGqw/mi
+# vCTeMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAJeIWagphw6ZiCdttSWey843
+# urlQ1jB18cjgNLsT+TcGskdNF0Fuln9ngz1HOcng+6hx5Phs37dve64HoV3FI71P
+# jURYWby1k3PIAzFJj0ZyUbj9MqjJqd1SjkrGlpNuUeq5PcdIWdLJePUHTMXhJpV2
+# KY60bntEaej6TgaqYMXz02tYn3mTEg5s4fTsoePjtf9Odcof1h5Rxj8ebd1uAVj6
+# o6B0512V/MLU1z98ZWQ8w3fvrfLLNp74wJUTHvJUM6UpwW5JwXosJ1Q6wLfNzeA3
+# a8W1Op41Ss6yM0eLIP2Ja+La8E+a4HCg+A+1WVkAuAawkWJrJg4HmPIAL4/u8boo
+# x/FB0dqVaCIF/SVEhZ6J2CtYPWotpC5H0qjlpfpYJKS9V/aq1+bxNjpDc6dl1gTU
+# jLXXlESshmPqdUdWNHXdPGxevF9/PkRlS5IQM87KTrbt0kQt2jfcDjtb+mWFA/0f
+# CRU8ouiRUJRO4Mcu2LPmGXXjqr24c0xSATQ1m9CpM7gwvLQAYfLHSNKV3UaTFh1W
+# hgkGhs7r2ib4asWjSKlDtnqexOdnokx8AFgnOeskGCY4g+GVqae+piPW/0gEsMdC
+# tG3pTLpfQxSfDjrmfCEZ3gLEPhiI4mQelEfi1mnzZ+UNCDoPsEUpuGqOdyzPEJCQ
+# yI6llccgfudjq9Objw6s
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/PSAppDeployToolkit.cer b/PSAppDeplyToolkit/PSAppDeployToolkit.cer
new file mode 100644
index 0000000..307aacd
--- /dev/null
+++ b/PSAppDeplyToolkit/PSAppDeployToolkit.cer
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIHSTCCBTGgAwIBAgIQCvlbtr6iDIUOmMb7jqwI+TANBgkqhkiG9w0BAQsFADBp
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMT
+OERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0
+IDIwMjEgQ0ExMB4XDTI0MDkwNTAwMDAwMFoXDTI3MDkwNzIzNTk1OVowgdExEzAR
+BgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAhMIQ29sb3JhZG8xHTAb
+BgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRQwEgYDVQQFEwsyMDEzMTYzODMy
+NzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCENvbG9yYWRvMRQwEgYDVQQHEwtDYXN0
+bGUgUm9jazEZMBcGA1UEChMQUGF0Y2ggTXkgUEMsIExMQzEZMBcGA1UEAxMQUGF0
+Y2ggTXkgUEMsIExMQzCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALsn
+cZKNh65erADSVI33cqSj+tKgR+RJIX2kUAJ5/nt74NnlXG4hFiI5azGM7ytrIDjA
+W8Bnm6gFEZBZlAig3RsXMSnrl3Wlzx1jysHNlo2AhWo61+h6H4osDczgnS+lRODw
+0IT0Ue0iHTTRUq8eQuGQzdU+jh/snV+xEBfPjQVDR0WxFXZfofR+QHscet2n2vM7
+t4Pxl5bslym2/iR7YDSWlIBbhTkU8cNUzuqh/kuh66aX/UHABZruMRrZHNhUoYL9
+DYFjDRg2aia/6PbKidrXWmRw8q+h/D72PHoKFLIRe3HIBGLRBHQfUkUfJlUIpNcO
+aBk4w1ox4/vI4E6c5XrUcsKbZP5vD3oVQTfJ7aqEnbyy3LkFc5rjy8zf4rioebGX
+lr6jzjQKXBJ2XDjaV3m8olD5xHj6+a2QFO4TIzMNmT50JTHGxr7YD9qou5tn95lx
+WMVo5SgsWgKWB3qkhXlgvMzOzmC9h5WfhriuFxvIylROrFklvVpP3ZtLyW2rLwID
+AQABo4ICAjCCAf4wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYD
+VR0OBBYEFORglN0hKniG4YWPXslNC3EyO+V/MD0GA1UdIAQ2MDQwMgYFZ4EMAQMw
+KTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1Ud
+DwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOg
+UaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRD
+b2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDov
+L2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdS
+U0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsG
+AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0
+dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVT
+aWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZI
+hvcNAQELBQADggIBAKgNLm/4pTIHSLzIgXlgaIjMXuTiG5TmxiO5XpnD9lhKmhAE
+ltdf8FcCVOt2cIbZEGjVOK143+n6suaTlM6UF4GI0mjuA/wDjCSh5cqcbJRamf3W
+KXLntsRNx+5ZjuCj3/FcV7hSFKoy3rVPpJIe6P0OdkWm1QLjqzxSpzm4sctRyMdP
++Rfkbj/cYapg23zO5ec1AHLjggpGO27riJxLIqfQWV1IlW/CuWz0fUZOw6GreBUJ
+je9sY2pHBGTjFP74NGYFWvJ8ZAV7VbI8W7K/mzg59HHXRytUB1opfz5qQDZMTex/
+LXQgGfG08yL77ncUi57e7LG20A5AMjcNG7Qx/jCr/5flXGMkB+dWecU/Q7xwphHe
+++G6GZD9hn0xb5+/4CEhI03TrlBrLXa4EsINcyT6oCu81sSuPMQu2sKWt4MDrPaZ
+8oqhxt68fOP0h1IgC9pZJY7A93qZkcbFnmYWTWPd8RKUB3vSwb6P7eFUY2c6lM/q
+XxDD6nl/4OfpqW+GqemZjSbgGCRZlNCyJAi0DfZil4tSJfVlOon5972LrRjEi/wX
+Xlj/u3zOzGS4jvtQSLAXUpleqWVUty0QQMt8CJW1i+vZr8iwjyEO8+HbX7s8At+h
+PZNr4c3og0PpNXRSQ0ncUw3rbHJNBbg9aL4YrtnGi+AXRbAlrFzyzMr7ujpW
+-----END CERTIFICATE-----
diff --git a/PSAppDeplyToolkit/PSAppDeployToolkit.psd1 b/PSAppDeplyToolkit/PSAppDeployToolkit.psd1
new file mode 100644
index 0000000..abac7c6
--- /dev/null
+++ b/PSAppDeplyToolkit/PSAppDeployToolkit.psd1
@@ -0,0 +1,498 @@
+#
+# Module manifest for module 'PSAppDeployToolkit'
+#
+# Generated on: 2024-04-13
+#
+
+@{
+ # Script module or binary module file associated with this manifest.
+ RootModule = 'PSAppDeployToolkit.psm1'
+
+ # Version number of this module.
+ ModuleVersion = '4.0.6'
+
+ # Supported PSEditions
+ # CompatiblePSEditions = @()
+
+ # ID used to uniquely identify this module
+ GUID = '8c3c366b-8606-4576-9f2d-4051144f7ca2'
+
+ # Author of this module
+ Author = 'PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough)'
+
+ # Company or vendor of this module
+ CompanyName = 'PSAppDeployToolkit Team'
+
+ # Copyright statement for this module
+ Copyright = 'Copyright © 2025 PSAppDeployToolkit Team. All rights reserved.'
+
+ # Description of the functionality provided by this module
+ Description = 'Enterprise App Deployment, Simplified.'
+
+ # Minimum version of the Windows PowerShell engine required by this module
+ PowerShellVersion = '5.1.14393.0'
+
+ # Name of the Windows PowerShell host required by this module
+ # PowerShellHostName = ''
+
+ # Minimum version of the Windows PowerShell host required by this module
+ # PowerShellHostVersion = ''
+
+ # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
+ DotNetFrameworkVersion = '4.6.2.0'
+
+ # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
+ CLRVersion = '4.0.30319.42000'
+
+ # Processor architecture (None, X86, Amd64) required by this module
+ ProcessorArchitecture = 'None'
+
+ # Modules that must be imported into the global environment prior to importing this module
+ # RequiredModules = @()
+
+ # Assemblies that must be loaded prior to importing this module
+ # RequiredAssemblies = @()
+
+ # Script files (.ps1) that are run in the caller's environment prior to importing this module.
+ # ScriptsToProcess = @()
+
+ # Type files (.ps1xml) to be loaded when importing this module
+ # TypesToProcess = @()
+
+ # Format files (.ps1xml) to be loaded when importing this module
+ # FormatsToProcess = @()
+
+ # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
+ # NestedModules = @()
+
+ # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
+ FunctionsToExport = @(
+ 'Add-ADTEdgeExtension'
+ 'Add-ADTSessionClosingCallback'
+ 'Add-ADTSessionFinishingCallback'
+ 'Add-ADTSessionOpeningCallback'
+ 'Add-ADTSessionStartingCallback'
+ 'Block-ADTAppExecution'
+ 'Close-ADTInstallationProgress'
+ 'Close-ADTSession'
+ 'Complete-ADTFunction'
+ 'Convert-ADTRegistryPath'
+ 'Convert-ADTValuesFromRemainingArguments'
+ 'Convert-ADTValueType'
+ 'ConvertTo-ADTNTAccountOrSID'
+ 'Copy-ADTContentToCache'
+ 'Copy-ADTFile'
+ 'Copy-ADTFileToUserProfiles'
+ 'Disable-ADTTerminalServerInstallMode'
+ 'Dismount-ADTWimFile'
+ 'Enable-ADTTerminalServerInstallMode'
+ 'Export-ADTEnvironmentTableToSessionState'
+ 'Get-ADTBoundParametersAndDefaultValues'
+ 'Get-ADTCommandTable'
+ 'Get-ADTConfig'
+ 'Get-ADTDeferHistory'
+ 'Get-ADTEnvironment'
+ 'Get-ADTEnvironmentTable'
+ 'Get-ADTFileVersion'
+ 'Get-ADTFreeDiskSpace'
+ 'Get-ADTIniValue'
+ 'Get-ADTApplication'
+ 'Get-ADTLoggedOnUser'
+ 'Get-ADTMsiExitCodeMessage'
+ 'Get-ADTMsiTableProperty'
+ 'Get-ADTObjectProperty'
+ 'Get-ADTPEFileArchitecture'
+ 'Get-ADTPendingReboot'
+ 'Get-ADTPowerShellProcessPath'
+ 'Get-ADTPresentationSettingsEnabledUsers'
+ 'Get-ADTRegistryKey'
+ 'Get-ADTRunAsActiveUser'
+ 'Get-ADTSchedulerTask'
+ 'Get-ADTServiceStartMode'
+ 'Get-ADTSession'
+ 'Get-ADTShortcut'
+ 'Get-ADTStringTable'
+ 'Get-ADTOperatingSystemInfo'
+ 'Get-ADTUniversalDate'
+ 'Get-ADTUserProfiles'
+ 'Get-ADTWindowTitle'
+ 'Initialize-ADTFunction'
+ 'Initialize-ADTModule'
+ 'Install-ADTMSUpdates'
+ 'Install-ADTSCCMSoftwareUpdates'
+ 'Invoke-ADTAllUsersRegistryAction'
+ 'Invoke-ADTCommandWithRetries'
+ 'Invoke-ADTRegSvr32'
+ 'Invoke-ADTFunctionErrorHandler'
+ 'Invoke-ADTObjectMethod'
+ 'Invoke-ADTSCCMTask'
+ 'Mount-ADTWimFile'
+ 'New-ADTErrorRecord'
+ 'New-ADTFolder'
+ 'New-ADTMsiTransform'
+ 'New-ADTShortcut'
+ 'New-ADTTemplate'
+ 'New-ADTValidateScriptErrorRecord'
+ 'New-ADTZipFile'
+ 'Open-ADTSession'
+ 'Out-ADTPowerShellEncodedCommand'
+ 'Register-ADTDll'
+ 'Remove-ADTContentFromCache'
+ 'Remove-ADTEdgeExtension'
+ 'Remove-ADTFile'
+ 'Remove-ADTFileFromUserProfiles'
+ 'Remove-ADTFolder'
+ 'Uninstall-ADTApplication'
+ 'Remove-ADTInvalidFileNameChars'
+ 'Remove-ADTRegistryKey'
+ 'Remove-ADTSessionClosingCallback'
+ 'Remove-ADTSessionFinishingCallback'
+ 'Remove-ADTSessionOpeningCallback'
+ 'Remove-ADTSessionStartingCallback'
+ 'Reset-ADTDeferHistory'
+ 'Resolve-ADTErrorRecord'
+ 'Send-ADTKeys'
+ 'Set-ADTActiveSetup'
+ 'Set-ADTDeferHistory'
+ 'Set-ADTIniValue'
+ 'Set-ADTItemPermission'
+ 'Set-ADTMsiProperty'
+ 'Set-ADTPowerShellCulture'
+ 'Set-ADTRegistryKey'
+ 'Set-ADTServiceStartMode'
+ 'Set-ADTShortcut'
+ 'Show-ADTBalloonTip'
+ 'Show-ADTDialogBox'
+ 'Show-ADTHelpConsole'
+ 'Show-ADTInstallationProgress'
+ 'Show-ADTInstallationPrompt'
+ 'Show-ADTInstallationRestartPrompt'
+ 'Show-ADTInstallationWelcome'
+ 'Start-ADTMsiProcess'
+ 'Start-ADTMspProcess'
+ 'Start-ADTProcess'
+ 'Start-ADTProcessAsUser'
+ 'Start-ADTServiceAndDependencies'
+ 'Stop-ADTServiceAndDependencies'
+ 'Test-ADTBattery'
+ 'Test-ADTCallerIsAdmin'
+ 'Test-ADTMicrophoneInUse'
+ 'Test-ADTModuleInitialized'
+ 'Test-ADTMSUpdates'
+ 'Test-ADTMutexAvailability'
+ 'Test-ADTNetworkConnection'
+ 'Test-ADTOobeCompleted'
+ 'Test-ADTPowerPoint'
+ 'Test-ADTRegistryValue'
+ 'Test-ADTServiceExists'
+ 'Test-ADTSessionActive'
+ 'Test-ADTUserIsBusy'
+ 'Unblock-ADTAppExecution'
+ 'Unregister-ADTDll'
+ 'Update-ADTDesktop'
+ 'Update-ADTEnvironmentPsProvider'
+ 'Update-ADTGroupPolicy'
+ 'Write-ADTLogEntry'
+ )
+
+ # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
+ CmdletsToExport = @()
+
+ # Variables to export from this module
+ # VariablesToExport = ''
+
+ # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
+ AliasesToExport = @()
+
+ # DSC resources to export from this module
+ # DscResourcesToExport = @()
+
+ # List of all modules packaged with this module
+ # ModuleList = @()
+
+ # List of all files packaged with this module
+ # FileList = @()
+
+ # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
+ PrivateData = @{
+
+ PSData = @{
+
+ # Tags applied to this module. These help with module discovery in online galleries.
+ Tags = 'psappdeploytoolkit', 'adt', 'psadt', 'appdeployment', 'appdeploytoolkit', 'appdeploy', 'deployment', 'toolkit'
+
+ # A URL to the license for this module.
+ LicenseUri = 'https://raw.githubusercontent.com/PSAppDeployToolkit/PSAppDeployToolkit/refs/heads/main/COPYING.Lesser'
+
+ # A URL to the main website for this project.
+ ProjectUri = 'https://psappdeploytoolkit.com'
+
+ # A URL to an icon representing this module.
+ IconUri = 'https://raw.githubusercontent.com/PSAppDeployToolkit/PSAppDeployToolkit/refs/heads/main/src/PSAppDeployToolkit/Assets/AppIcon.png'
+
+ # ReleaseNotes of this module
+ ReleaseNotes = 'https://github.com/psappdeploytoolkit/psappdeploytoolkit/releases/latest'
+
+ # Prerelease tag for PSGallery.
+ # Prerelease = 'beta1'
+
+ } # End of PSData hashtable
+
+ } # End of PrivateData hashtable
+
+ # HelpInfo URI of this module
+ # HelpInfoURI = ''
+
+ # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
+ # DefaultCommandPrefix = ''
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCADZ3ospIJInxQM
+# NqU+hKS8u5XCPoQeEe1N6qFgko3PgaCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgi14izbsoHr4Lxd2DrscvEK/kOFxnzWu6DjnauwhV2Now
+# DQYJKoZIhvcNAQEBBQAEggGAs705CaFyevyskoeoU2Wxzkil87rLLK6qIOifuHgx
+# oxJj0qgqGMlL4YOHfn+sFIwn8blR4OAQGzsycyEfipJrM8IoCHZDncue+voqua7b
+# /K93gXmlPryOeBDNkveGQECpq+iwrVRJGkgPyrULlRLh3/baSM6HKOklCCAwrKM1
+# U+l6fn65rtT9n3cQRyQo4o1alUwsuO75fuNspSQ/uXY1M0QILO5/3k6bJAmwt1ez
+# STpsNbC4WBIvSTiOTHvZ/05hr+9kaMgGsg3qO+JvvtqlzuU+zXJWW6Is2TOgmgy6
+# VXQtoIU9d/qZPsAlJocVGtaBCOpt8cRR3L853au4JrqFFWktKQ+e4IWr8igKKxc/
+# R/SN6bRaKmQdLBoTyNcYGKP1yOHNZHVp/F97oC7nfA48vMQ817UtGvFzxNggPGYP
+# uBNW83UN86OArl0fTdKAI4k4ekGa19FFEQVcocLhNEXMnegvgToZkTEotQFpkG7U
+# hXgx80rNeKmczaw5QO50fs5ZoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIE5Y2/xk
+# suTwmJHx1xLvx3O+v+7O58yk0XoIRvYsfLPOAhBzrj7mByz5FFgccF7FQo6yGA8y
+# MDI1MDIyMzIzNDgzOFqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgzOFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEICAp+JtDZb+qi+kPRSz1ishhB3lAbHjDcXDUnUGY
+# xduwMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAH19Xf8230TmkJse5GUWuCf+
+# KzkIOTfvQS+qywr1uGPhBY8yIGdEREEI8IUyHXZVXyzJXQHq19r5h4+6GKqPx+fo
+# rBu2Jg43yztRNUXGMUIxTAUCUL9oG65t/QPztsqEUAg6oHVbZ7mr8Q0S8mGMj7nY
+# lw1WRd12fSf2I1dZvmJhZFyMIp2YqueMTjUqsx+JfW2+m4E/rO/bJrp0B3vMJ37B
+# 8dk6s2j96wWBj9Y19jemhPtoNBCSiQK69H8dn0Lfx0iw0vtCR4ZBbkBFLoKBKbgC
+# dMu09++UuhbaBRlkUiCccp8t4wy1Kgc0FmGnZHmVP8Y3U4LotxDeI5Y0g9WNxw55
+# AbfRwBG8ooOKR4AqBtnelx7xdVkSyF6k1ckv3FzwIkZ+WK+A3l+QGHGDXGqiVHpm
+# iEn66kCye0YYni2I1EnqWI3HyfORo70zo6lyUK4xZAVZhjwoZeCtDHyfnqjC5/P6
+# KUp+JM0FLos+h8G24ztUUA+kNE7xGahMbjr9XWhVH9NtfNgjR3Ym8fX7DLv/GbiL
+# u7zr01W5BAwYjn6W9BwQ0uXGjG4w7Pe+NZMpRhgPxBGmsl4ykGE0Z1WEB32CP40T
+# +6DleduRrSlR1R2vmo4dpEj/iOXVqSfPLIYjkyGgPZD62rXuhZE2D0W5TeJQ+GgH
+# m0JABr2NOMpwbD1aUC+T
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/PSAppDeployToolkit.psm1 b/PSAppDeplyToolkit/PSAppDeployToolkit.psm1
new file mode 100644
index 0000000..df1a2d1
--- /dev/null
+++ b/PSAppDeplyToolkit/PSAppDeployToolkit.psm1
@@ -0,0 +1,23611 @@
+<#
+
+.SYNOPSIS
+PSAppDeployToolkit - This module script contains the PSADT core runtime and functions using by a Invoke-AppDeployToolkit.ps1 script.
+
+.DESCRIPTION
+This module can be directly imported from the command line via Import-Module, but it is usually imported by the Invoke-AppDeployToolkit.ps1 script.
+
+This module can usually be updated to the latest version without impacting your per-application Invoke-AppDeployToolkit.ps1 scripts. Please check release notes before upgrading.
+
+PSAppDeployToolkit is licensed under the GNU LGPLv3 License - (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation, either version 3 of the License, or 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 Lesser General Public License along with this program. If not, see .
+
+.LINK
+https://psappdeploytoolkit.com
+
+#>
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Module Initialization Code
+#
+#-----------------------------------------------------------------------------
+
+# Throw if this psm1 file isn't being imported via our manifest.
+if (!([System.Environment]::StackTrace.Split("`n") -like '*Microsoft.PowerShell.Commands.ModuleCmdletBase.LoadModuleManifest(*'))
+{
+ throw [System.Management.Automation.ErrorRecord]::new(
+ [System.InvalidOperationException]::new("This module must be imported via its .psd1 file, which is recommended for all modules that supply a .psd1 file."),
+ 'ModuleImportError',
+ [System.Management.Automation.ErrorCategory]::InvalidOperation,
+ $MyInvocation.MyCommand.ScriptBlock.Module
+ )
+}
+
+# Clock when the module import starts so we can track it.
+[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'ModuleImportStart', Justification = "This variable is used within ImportsLast.ps1 and therefore cannot be seen here.")]
+$ModuleImportStart = [System.DateTime]::Now
+
+# Rethrowing caught exceptions makes the error output from Import-Module look better.
+try
+{
+ # Determine if we're doing a minimal startup (i.e. on Linux but want to allow New-ADTTemplate to funcion).
+ $MinimumStartup = $PSEdition.Equals('Core') -and !$IsWindows
+
+ # Build out lookup table for all cmdlets used within module, starting with the core cmdlets.
+ $CommandTable = [System.Collections.Generic.Dictionary[System.String, System.Management.Automation.CommandInfo]]::new()
+ $ExecutionContext.SessionState.InvokeCommand.GetCmdlets() | & { process { if ($_.PSSnapIn -and $_.PSSnapIn.Name.Equals('Microsoft.PowerShell.Core') -and $_.PSSnapIn.IsDefault) { $CommandTable.Add($_.Name, $_) } } }
+
+ # Expand command lookup table with cmdlets used through this module.
+ & {
+ $RequiredModules = [System.Collections.ObjectModel.ReadOnlyCollection[Microsoft.PowerShell.Commands.ModuleSpecification]]$(
+ if (!$MinimumStartup)
+ {
+ @{ ModuleName = 'CimCmdlets'; Guid = 'fb6cc51d-c096-4b38-b78d-0fed6277096a'; ModuleVersion = '1.0' }
+ @{ ModuleName = 'Dism'; Guid = '389c464d-8b8d-48e9-aafe-6d8a590d6798'; ModuleVersion = '1.0' }
+ @{ ModuleName = 'International'; Guid = '561544e6-3a83-4d24-b140-78ad771eaf10'; ModuleVersion = '1.0' }
+ @{ ModuleName = 'NetAdapter'; Guid = '1042b422-63a8-4016-a6d6-293e19e8f8a6'; ModuleVersion = '1.0' }
+ @{ ModuleName = 'ScheduledTasks'; Guid = '5378ee8e-e349-49bb-83b9-f3d9c396c0a6'; ModuleVersion = '1.0' }
+ }
+ @{ ModuleName = 'Microsoft.PowerShell.Archive'; Guid = 'eb74e8da-9ae2-482a-a648-e96550fb8733'; ModuleVersion = '1.0' }
+ @{ ModuleName = 'Microsoft.PowerShell.Management'; Guid = 'eefcb906-b326-4e99-9f54-8b4bb6ef3c6d'; ModuleVersion = '1.0' }
+ @{ ModuleName = 'Microsoft.PowerShell.Security'; Guid = 'a94c8c7e-9810-47c0-b8af-65089c13a35a'; ModuleVersion = '1.0' }
+ @{ ModuleName = 'Microsoft.PowerShell.Utility'; Guid = '1da87e53-152b-403e-98dc-74d7b4d63d59'; ModuleVersion = '1.0' }
+ )
+ (& $Script:CommandTable.'Import-Module' -FullyQualifiedName $RequiredModules -Global -Force -PassThru -ErrorAction Stop).ExportedCommands.Values | & { process { $CommandTable.Add($_.Name, $_) } }
+ }
+
+ # Set required variables to ensure module functionality.
+ & $Script:CommandTable.'New-Variable' -Name ErrorActionPreference -Value ([System.Management.Automation.ActionPreference]::Stop) -Option Constant -Force
+ & $Script:CommandTable.'New-Variable' -Name InformationPreference -Value ([System.Management.Automation.ActionPreference]::Continue) -Option Constant -Force
+ & $Script:CommandTable.'New-Variable' -Name ProgressPreference -Value ([System.Management.Automation.ActionPreference]::SilentlyContinue) -Option Constant -Force
+
+ # Ensure module operates under the strictest of conditions.
+ & $Script:CommandTable.'Set-StrictMode' -Version 3
+
+ # Throw if any previous version of the unofficial PSADT module is found on the system.
+ if (& $Script:CommandTable.'Get-Module' -FullyQualifiedName @{ ModuleName = 'PSADT'; Guid = '41b2dd67-8447-4c66-b08a-f0bd0d5458b9'; ModuleVersion = '1.0' } -ListAvailable -Refresh)
+ {
+ & $Script:CommandTable.'Write-Warning' -Message "This module should not be used while the unofficial v3 PSADT module is installed."
+ }
+
+ # Store build information pertaining to this module's state.
+ & $Script:CommandTable.'New-Variable' -Name Module -Option Constant -Force -Value ([ordered]@{
+ Manifest = & $Script:CommandTable.'Import-LocalizedData' -BaseDirectory $PSScriptRoot -FileName 'PSAppDeployToolkit.psd1'
+ Assemblies = (& $Script:CommandTable.'Get-ChildItem' -LiteralPath $PSScriptRoot\lib -File -Filter PSADT*.dll).FullName
+ Compiled = $MyInvocation.MyCommand.Name.Equals('PSAppDeployToolkit.psm1')
+ Signed = $(if (!$MinimumStartup) { (& $Script:CommandTable.'Get-AuthenticodeSignature' -LiteralPath $MyInvocation.MyCommand.Path).Status.Equals([System.Management.Automation.SignatureStatus]::Valid) })
+ }).AsReadOnly()
+
+ # Perform remaining Windows setup.
+ if (!$MinimumStartup)
+ {
+ # Import our assemblies, factoring in whether they're on a network share or not.
+ $Module.Assemblies | & {
+ begin
+ {
+ # Cache loaded assemblies to test whether they're already loaded.
+ $domainAssemblies = [System.AppDomain]::CurrentDomain.GetAssemblies()
+
+ # Determine whether we're on a network location.
+ $isNetworkLocation = [System.Uri]::new($PSScriptRoot).IsUnc -or ($PSScriptRoot -match '^([A-Za-z]:)\\' -and ((& $Script:CommandTable.'Get-CimInstance' -ClassName Win32_LogicalDisk -Filter "DeviceID='$($Matches[1])'").ProviderName -match '^\\\\'))
+
+ # Add in system assemblies.
+ & $Script:CommandTable.'Add-Type' -AssemblyName @(
+ 'System.ServiceProcess'
+ 'System.Drawing'
+ 'System.Windows.Forms'
+ 'PresentationCore'
+ 'PresentationFramework'
+ 'WindowsBase'
+ )
+ }
+
+ process
+ {
+ # Test whether the assembly is already loaded.
+ if (($existingAssembly = $domainAssemblies | & { process { if ([System.IO.Path]::GetFileName($_.Location).Equals([System.IO.Path]::GetFileName($args[0]))) { return $_ } } } $_ | & $Script:CommandTable.'Select-Object' -First 1))
+ {
+ # Test the loaded assembly for SHA256 hash equality, returning early if the assembly is OK.
+ if (!(& $Script:CommandTable.'Get-FileHash' -LiteralPath $existingAssembly.Location).Hash.Equals((& $Script:CommandTable.'Get-FileHash' -LiteralPath $_).Hash))
+ {
+ throw [System.Management.Automation.ErrorRecord]::new(
+ [System.InvalidOperationException]::new("A PSAppDeployToolkit assembly of a different file hash is already loaded. Please restart PowerShell and try again."),
+ 'ConflictingModuleLoaded',
+ [System.Management.Automation.ErrorCategory]::InvalidOperation,
+ $existingAssembly
+ )
+ }
+ return
+ }
+
+ # If we're on a compiled build, confirm the DLLs are signed before proceeding.
+ if ($Module.Signed -and !($badFile = & $Script:CommandTable.'Get-AuthenticodeSignature' -LiteralPath $_).Status.Equals([System.Management.Automation.SignatureStatus]::Valid))
+ {
+ throw [System.Management.Automation.ErrorRecord]::new(
+ [System.InvalidOperationException]::new("The assembly [$_] has an invalid digital signature and cannot be loaded."),
+ 'ADTAssemblyFileSignatureError',
+ [System.Management.Automation.ErrorCategory]::SecurityError,
+ $badFile
+ )
+ }
+
+ # If loading from an SMB path, load unsafely. This is OK because in signed (release) modules, we're validating the signature above.
+ if ($isNetworkLocation)
+ {
+ [System.Reflection.Assembly]::UnsafeLoadFrom($_)
+ }
+ else
+ {
+ & $Script:CommandTable.'Add-Type' -LiteralPath $_
+ }
+ }
+ }
+
+ # Set the process as HiDPI so long as we're in a real console.
+ if ($Host.Name.Equals('ConsoleHost'))
+ {
+ try
+ {
+ [PSADT.GUI.UiAutomation]::SetProcessDpiAwarenessForOSVersion()
+ }
+ catch
+ {
+ $null = $null
+ }
+ }
+
+ # All WinForms-specific initialization code.
+ try
+ {
+ [System.Windows.Forms.Application]::EnableVisualStyles()
+ [System.Windows.Forms.Application]::SetCompatibleTextRenderingDefault($false)
+ }
+ catch
+ {
+ $null = $null
+ }
+ }
+
+ # Remove any previous functions that may have been defined.
+ if ($Module.Compiled)
+ {
+ & $Script:CommandTable.'New-Variable' -Name FunctionPaths -Option Constant -Value ($MyInvocation.MyCommand.ScriptBlock.Ast.EndBlock.Statements | & { process { if ($_ -is [System.Management.Automation.Language.FunctionDefinitionAst]) { return "Microsoft.PowerShell.Core\Function::$($_.Name)" } } })
+ & $Script:CommandTable.'Remove-Item' -LiteralPath $FunctionPaths -Force -ErrorAction Ignore
+ }
+}
+catch
+{
+ throw
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Close-ADTInstallationProgressClassic
+#
+#-----------------------------------------------------------------------------
+
+function Close-ADTInstallationProgressClassic
+{
+ # Process the WPF window if it exists.
+ if ($Script:Dialogs.Classic.ProgressWindow.SyncHash.ContainsKey('Window'))
+ {
+ if (!$Script:Dialogs.Classic.ProgressWindow.Invocation.IsCompleted)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Closing the installation progress dialog.'
+ $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window.Dispatcher.Invoke({ $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window.Close() }, [System.Windows.Threading.DispatcherPriority]::Send)
+ while (!$Script:Dialogs.Classic.ProgressWindow.Invocation.IsCompleted) {}
+ }
+ $Script:Dialogs.Classic.ProgressWindow.SyncHash.Clear()
+ }
+
+ # End the PowerShell instance if it's invoked.
+ if ($Script:Dialogs.Classic.ProgressWindow.Invocation)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Closing the installation progress dialog's invocation."
+ $null = $Script:Dialogs.Classic.ProgressWindow.PowerShell.EndInvoke($Script:Dialogs.Classic.ProgressWindow.Invocation)
+ $Script:Dialogs.Classic.ProgressWindow.Invocation = $null
+ }
+
+ # Process the PowerShell window.
+ if ($Script:Dialogs.Classic.ProgressWindow.PowerShell)
+ {
+ # Close down the runspace.
+ if ($Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace -and $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.RunspaceStateInfo.State.Equals([System.Management.Automation.Runspaces.RunspaceState]::Opened))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Closing the installation progress dialog's runspace."
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.Close()
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.Dispose()
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace = $null
+ }
+
+ # Dispose of remaining PowerShell variables.
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Dispose()
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell = $null
+ }
+
+ # Reset the state bool.
+ $Script:Dialogs.Classic.ProgressWindow.Running = $false
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Close-ADTInstallationProgressFluent
+#
+#-----------------------------------------------------------------------------
+
+function Close-ADTInstallationProgressFluent
+{
+ # Hide the dialog and reset the state bool.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Closing the installation progress dialog.'
+ [PSADT.UserInterface.UnifiedADTApplication]::CloseProgressDialog()
+ $Script:Dialogs.Fluent.ProgressWindow.Running = $false
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Convert-RegistryKeyToHashtable
+#
+#-----------------------------------------------------------------------------
+
+function Convert-RegistryKeyToHashtable
+{
+ begin
+ {
+ # Open collector to store all converted keys.
+ $data = @{}
+ }
+
+ process
+ {
+ # Process potential subkeys first.
+ $subdata = $_ | & $Script:CommandTable.'Get-ChildItem' | & $MyInvocation.MyCommand
+
+ # Open a new subdata hashtable if we had no subkeys.
+ if ($null -eq $subdata)
+ {
+ $subdata = @{}
+ }
+
+ # Process this item and store its values.
+ $_ | & $Script:CommandTable.'Get-ItemProperty' | & {
+ process
+ {
+ $_.PSObject.Properties | & {
+ process
+ {
+ if (($_.Name -notmatch '^PS((Parent)?Path|ChildName|Provider)$') -and ![System.String]::IsNullOrWhiteSpace((& $Script:CommandTable.'Out-String' -InputObject $_.Value)))
+ {
+ # Handle bools as string values.
+ if ($_.Value -match '^(True|False)$')
+ {
+ $subdata.Add($_.Name, [System.Boolean]::Parse($_.Value))
+ }
+ else
+ {
+ $subdata.Add($_.Name, $_.Value)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ # Add the subdata to the sections if it's got a count.
+ if ($subdata.Count)
+ {
+ $data.Add($_.PSPath -replace '^.+\\', $subdata)
+ }
+ }
+
+ end
+ {
+ # If there's something in the collector, return it.
+ if ($data.Count)
+ {
+ return $data
+ }
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Disable-ADTWindowCloseButton
+#
+#-----------------------------------------------------------------------------
+
+function Disable-ADTWindowCloseButton
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (($null -eq $_) -or $_.Equals([System.IntPtr]::Zero))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName WindowHandle -ProvidedValue $_ -ExceptionMessage 'The provided window handle is invalid.'))
+ }
+ return !!$_
+ })]
+ [System.IntPtr]$WindowHandle
+ )
+
+ $null = if (($menuHandle = [PSADT.LibraryInterfaces.User32]::GetSystemMenu($WindowHandle, $false)) -and ($menuHandle -ne [System.IntPtr]::Zero))
+ {
+ [PSADT.LibraryInterfaces.User32]::EnableMenuItem($menuHandle, 0xF060, 0x00000001)
+ [PSADT.LibraryInterfaces.User32]::DestroyMenu($menuHandle)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Exit-ADTInvocation
+#
+#-----------------------------------------------------------------------------
+
+function Exit-ADTInvocation
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$ExitCode,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoShellExit,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Force
+ )
+
+ # Attempt to close down any progress dialog here as an additional safety item.
+ $progressOpen = if (& $Script:CommandTable.'Test-ADTInstallationProgressRunning')
+ {
+ try
+ {
+ & $Script:CommandTable.'Close-ADTInstallationProgress'
+ }
+ catch
+ {
+ $_
+ }
+ }
+
+ # Flag the module as uninitialized upon last session closure.
+ $Script:ADT.Initialized = $false
+
+ # Return early if this function was called from the command line.
+ if ($NoShellExit -and !$Force)
+ {
+ $Global:LASTEXITCODE = $ExitCode
+ break
+ }
+
+ # If a callback failed and we're in a proper console, forcibly exit the process.
+ # The proper closure of a blocking dialog can stall a traditional exit indefinitely.
+ if ($Force -or ($Host.Name.Equals('ConsoleHost') -and $progressOpen))
+ {
+ [System.Environment]::Exit($ExitCode)
+ }
+ exit $ExitCode
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTEdgeExtensions
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTEdgeExtensions
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ param
+ (
+ )
+
+ # Check if the ExtensionSettings registry key exists. If not, create it.
+ if (!(& $Script:CommandTable.'Test-ADTRegistryValue' -Key Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge -Name ExtensionSettings))
+ {
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge -Name ExtensionSettings -Value "" | & $Script:CommandTable.'Out-Null'
+ return [pscustomobject]@{}
+ }
+ $extensionSettings = & $Script:CommandTable.'Get-ADTRegistryKey' -Key Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge -Name ExtensionSettings
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Configured extensions: [$($extensionSettings)]." -Severity 1
+ return $extensionSettings | & $Script:CommandTable.'ConvertFrom-Json'
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTMountedWimFile
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTMountedWimFile
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ImagePath', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Path', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ [OutputType([Microsoft.Dism.Commands.MountedImageInfoObject])]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'ImagePath')]
+ [ValidateNotNullOrEmpty()]
+ [System.IO.FileInfo[]]$ImagePath,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.IO.DirectoryInfo[]]$Path
+ )
+
+ # Get the caller's provided input via the ParameterSetName so we can filter on its name and value.
+ $parameter = & $Script:CommandTable.'Get-Variable' -Name $PSCmdlet.ParameterSetName
+ return (& $Script:CommandTable.'Get-WindowsImage' -Mounted | & { process { if ($parameter.Value.FullName.Contains($_.($parameter.Name))) { return $_ } } })
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTParentProcesses
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTParentProcesses
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ param
+ (
+ )
+
+ # Open object to store all parents for returning. This also avoids an infinite loop situation.
+ $parents = [System.Collections.Generic.List[Microsoft.Management.Infrastructure.CimInstance]]::new()
+
+ # Get all processes from the system. WMI consistently gives us the parent on PowerShell 5.x and Core targets.
+ $processes = & $Script:CommandTable.'Get-CimInstance' -ClassName Win32_Process
+ $process = $processes | & { process { if ($_.ProcessId -eq $PID) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1
+
+ # Get all parents for the currently stored process.
+ while ($process = $processes | & { process { if ($_.ProcessId -eq $process.ParentProcessId) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1)
+ {
+ if ($parents.Contains($process))
+ {
+ break
+ }
+ $parents.Add($process)
+ }
+
+ # Return all parents to the caller.
+ return $parents
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTProcessHandles
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTProcessHandles
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ param
+ (
+ )
+
+ # Get CSV data from the binary and confirm success.
+ $exeHandle = "$Script:PSScriptRoot\bin\$([PSADT.OperatingSystem.OSHelper]::GetArchitecture())\handle\handle.exe"
+ $exeHandleResults = & $exeHandle -accepteula -nobanner -v 2>&1
+ if ($Global:LASTEXITCODE -ne 0)
+ {
+ $naerParams = @{
+ Exception = [System.Runtime.InteropServices.ExternalException]::new("The call to [$exeHandle] failed with exit code [$Global:LASTEXITCODE].", $Global:LASTEXITCODE)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'HandleExecutableFailure'
+ TargetObject = $exeHandleResults
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Convert CSV data to objects and re-process to remove non-word characters before returning data to the caller.
+ if (($handles = $exeHandleResults | & $Script:CommandTable.'ConvertFrom-Csv'))
+ {
+ return $handles | & $Script:CommandTable.'Select-Object' -Property ($handles[0].PSObject.Properties.Name | & {
+ process
+ {
+ @{ Label = $_ -replace '[^\w]'; Expression = [scriptblock]::Create("`$_.'$_'.Trim()") }
+ }
+ })
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTRunningProcesses
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTRunningProcesses
+{
+ <#
+
+ .SYNOPSIS
+ Gets the processes that are running from a custom list of process objects and also adds a property called ProcessDescription.
+
+ .DESCRIPTION
+ Gets the processes that are running from a custom list of process objects and also adds a property called ProcessDescription.
+
+ .PARAMETER ProcessObjects
+ Custom object containing the process objects to search for.
+
+ .INPUTS
+ None. You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Diagnostics.Process. Returns one or more process objects representing each running process found.
+
+ .EXAMPLE
+ Get-ADTRunningProcesses -ProcessObjects $processObjects
+
+ .NOTES
+ This is an internal script function and should typically not be called directly.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ .LINK
+ https://psappdeploytoolkit.com
+
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ [OutputType([System.Diagnostics.Process])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [AllowNull()][AllowEmptyCollection()]
+ [PSADT.Types.ProcessObject[]]$ProcessObjects
+ )
+
+ # Return early if we've received no input.
+ if ($null -eq $ProcessObjects)
+ {
+ return
+ }
+
+ # Get all running processes and append properties.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Checking for running applications: [$($ProcessObjects.Name -join ',')]"
+ $runningProcesses = & $Script:CommandTable.'Get-Process' -Name $ProcessObjects.Name -ErrorAction Ignore | & {
+ process
+ {
+ if (!$_.HasExited)
+ {
+ return $_ | & $Script:CommandTable.'Add-Member' -MemberType NoteProperty -Name ProcessDescription -Force -PassThru -Value $(
+ if (![System.String]::IsNullOrWhiteSpace(($objDescription = $ProcessObjects | & $Script:CommandTable.'Where-Object' -Property Name -EQ -Value $_.ProcessName | & $Script:CommandTable.'Select-Object' -ExpandProperty Description -ErrorAction Ignore)))
+ {
+ # The description of the process provided with the object.
+ $objDescription
+ }
+ elseif ($_.Description)
+ {
+ # If the process already has a description field specified, then use it.
+ $_.Description
+ }
+ else
+ {
+ # Fall back on the process name if no description is provided by the process or as a parameter to the function.
+ $_.ProcessName
+ }
+ )
+ }
+ }
+ }
+
+ # Return output if there's any.
+ if ($runningProcesses)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The following processes are running: [$(($runningProcesses.ProcessName | & $Script:CommandTable.'Select-Object' -Unique) -join ',')]."
+ return ($runningProcesses | & $Script:CommandTable.'Sort-Object' -Property ProcessDescription)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Specified applications are not running.'
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTSCCMClientVersion
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTSCCMClientVersion
+{
+ # Make sure SCCM client is installed and running.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Checking to see if SCCM Client service [ccmexec] is installed and running.'
+ if (!(& $Script:CommandTable.'Test-ADTServiceExists' -Name ccmexec))
+ {
+ $naerParams = @{
+ Exception = [System.ApplicationException]::new('SCCM Client Service [ccmexec] does not exist. The SCCM Client may not be installed.')
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'CcmExecServiceMissing'
+ RecommendedAction = "Please check the availability of this service and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ if (($svc = & $Script:CommandTable.'Get-Service' -Name ccmexec).Status -ne 'Running')
+ {
+ $naerParams = @{
+ Exception = [System.ApplicationException]::new("SCCM Client Service [ccmexec] exists but it is not in a 'Running' state.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'CcmExecServiceNotRunning'
+ TargetObject = $svc
+ RecommendedAction = "Please check the status of this service and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Determine the SCCM Client Version.
+ try
+ {
+ [System.Version]$SCCMClientVersion = & $Script:CommandTable.'Get-CimInstance' -Namespace ROOT\CCM -ClassName CCM_InstalledComponent | & { process { if ($_.Name -eq 'SmsClient') { $_.Version } } }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to query the system for the SCCM client version number.`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 2
+ throw
+ }
+ if (!$SCCMClientVersion)
+ {
+ $naerParams = @{
+ Exception = [System.Data.NoNullAllowedException]::new('The query for the SmsClient version returned a null result.')
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'CcmExecVersionNullOrEmpty'
+ RecommendedAction = "Please check the installed version and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Installed SCCM Client Version Number [$SCCMClientVersion]."
+ return $SCCMClientVersion
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTSessionCacheScriptDirectory
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTSessionCacheScriptDirectory
+{
+ # Determine whether we've got a valid script directory for caching purposes and throw if we don't.
+ $scriptDir = if (($adtSession = & $Script:CommandTable.'Get-ADTSession').ScriptDirectory -and $adtSession.ScriptDirectory.Count)
+ {
+ if ($adtSession.ScriptDirectory.Count -gt 1)
+ {
+ $adtSession.ScriptDirectory | & { process { if ([System.IO.Directory]::Exists([System.IO.Path]::Combine($_, 'Files'))) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1
+ }
+ elseif ([System.IO.Directory]::Exists([System.IO.Path]::Combine($($adtSession.ScriptDirectory), 'Files')))
+ {
+ $($adtSession.ScriptDirectory)
+ }
+ elseif ($adtSession.DirFiles -and [System.IO.Directory]::Exists($adtSession.DirFiles))
+ {
+ [System.IO.DirectoryInfo]::new($adtSession.DirFiles).Parent.FullName
+ }
+ }
+ if (!$scriptDir)
+ {
+ $naerParams = @{
+ Exception = [System.IO.DirectoryNotFoundException]::new("None of the current session's ScriptDirectory paths contain any Files/SupportFiles directories.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'ScriptDirectoryInvalid'
+ TargetObject = $adtSession.ScriptDirectory
+ RecommendedAction = "Please review the session's ScriptDirectory listing, then try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ return $scriptDir
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTStringLanguage
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTStringLanguage
+{
+ if (![System.String]::IsNullOrWhiteSpace(($adtConfig = & $Script:CommandTable.'Get-ADTConfig').UI.LanguageOverride))
+ {
+ # The caller has specified a specific language.
+ return $adtConfig.UI.LanguageOverride
+ }
+ else
+ {
+ # Fall back to PowerShell's.
+ return [System.Threading.Thread]::CurrentThread.CurrentUICulture
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Import-ADTConfig
+#
+#-----------------------------------------------------------------------------
+
+function Import-ADTConfig
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName BaseDirectory -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (![System.IO.Directory]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName BaseDirectory -ProvidedValue $_ -ExceptionMessage 'The specified directory does not exist.'))
+ }
+ return $_
+ })]
+ [System.String[]]$BaseDirectory
+ )
+
+ # Internal filter to process asset file paths.
+ filter Update-ADTAssetFilePath
+ {
+ # Go recursive if we've received a hashtable, otherwise just update the values.
+ foreach ($asset in $($_.GetEnumerator()))
+ {
+ # Re-process if this is a hashtable.
+ if ($asset.Value -is [System.Collections.Hashtable])
+ {
+ $asset.Value | & $MyInvocation.MyCommand; continue
+ }
+
+ # Skip if the path is fully qualified.
+ if ([System.IO.Path]::IsPathRooted($asset.Value))
+ {
+ continue
+ }
+
+ # Get the asset's full path based on the supplied BaseDirectory.
+ # Fall back to the module's path if the asset is unable to be found.
+ $assetPath = foreach ($directory in $($BaseDirectory[($BaseDirectory.Count - 1)..(0)]; $Script:ADT.Directories.Defaults.Config))
+ {
+ if (($assetPath = & $Script:CommandTable.'Get-Item' -LiteralPath "$directory\$($_.($asset.Key))" -ErrorAction Ignore))
+ {
+ $assetPath.FullName
+ break
+ }
+ }
+
+ # Throw if we found no asset.
+ if (!$assetPath)
+ {
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("Failed to resolve the asset [$($asset.Key)] to a valid file path.", $_.($asset.Key))
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'DialogAssetNotFound'
+ TargetObject = $_.($asset.Key)
+ RecommendedAction = "Ensure the file exists and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ $_.($asset.Key) = $assetPath
+ }
+ }
+
+ # Internal filter to expand variables.
+ filter Expand-ADTVariablesInConfig
+ {
+ # Go recursive if we've received a hashtable, otherwise just update the values.
+ foreach ($section in $($_.GetEnumerator()))
+ {
+ if ($section.Value -is [System.Collections.Hashtable])
+ {
+ $section.Value | & $MyInvocation.MyCommand
+ }
+ elseif ($section.Value -is [System.String])
+ {
+ $_.($section.Key) = $ExecutionContext.InvokeCommand.ExpandString($section.Value)
+ }
+ }
+ }
+
+ # Import the config from disk.
+ $config = & $Script:CommandTable.'Import-ADTModuleDataFile' @PSBoundParameters -FileName config.psd1
+
+ # Place restrictions on non-ConsoleHost targets.
+ if ($Host.Name.Equals('Windows PowerShell ISE Host'))
+ {
+ $config.UI.DialogStyle = 'Classic'
+ }
+
+ # Confirm the specified dialog type is valid.
+ if (($config.UI.DialogStyle -ne 'Classic') -and (& $Script:CommandTable.'Test-ADTNonNativeCaller'))
+ {
+ $config.UI.DialogStyle = if ($config.UI.ContainsKey('DialogStyleCompatMode'))
+ {
+ $config.UI.DialogStyleCompatMode
+ }
+ else
+ {
+ 'Classic'
+ }
+ }
+ if (!$Script:Dialogs.Contains($config.UI.DialogStyle))
+ {
+ $naerParams = @{
+ Exception = [System.NotSupportedException]::new("The specified dialog style [$($config.UI.DialogStyle)] is not valid. Valid styles are ['$($Script:Dialogs.Keys -join "', '")'].")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidData
+ ErrorId = 'DialogStyleInvalid'
+ TargetObject = $config
+ RecommendedAction = "Please review the supplied configuration file and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+
+ # Expand out environment variables and asset file paths.
+ ($adtEnv = & $Script:CommandTable.'Get-ADTEnvironmentTable').GetEnumerator() | & { process { & $Script:CommandTable.'New-Variable' -Name $_.Name -Value $_.Value -Option Constant } end { $config | Expand-ADTVariablesInConfig } }
+ $config.Assets | Update-ADTAssetFilePath
+
+ # Set the app's AUMID so it doesn't just say "Windows PowerShell".
+ if ($config.UI.BalloonNotifications -and ![PSADT.LibraryInterfaces.Shell32]::SetCurrentProcessExplicitAppUserModelID($config.UI.BalloonTitle))
+ {
+ $regKey = "$(if ($adtEnv.IsAdmin) { 'HKEY_CLASSES_ROOT' } else { 'HKEY_CURRENT_USER\Software\Classes' })\AppUserModelId\$($config.UI.BalloonTitle)"
+ [Microsoft.Win32.Registry]::SetValue($regKey, 'DisplayName', $config.UI.BalloonTitle, [Microsoft.Win32.RegistryValueKind]::String)
+ [Microsoft.Win32.Registry]::SetValue($regKey, 'IconUri', $config.Assets.Logo, [Microsoft.Win32.RegistryValueKind]::ExpandString)
+ }
+
+ # Change paths to user accessible ones if user isn't an admin.
+ if (!$adtEnv.IsAdmin)
+ {
+ if (![System.String]::IsNullOrWhiteSpace($config.Toolkit.TempPathNoAdminRights))
+ {
+ $config.Toolkit.TempPath = $config.Toolkit.TempPathNoAdminRights
+ }
+ if (![System.String]::IsNullOrWhiteSpace($config.Toolkit.RegPathNoAdminRights))
+ {
+ $config.Toolkit.RegPath = $config.Toolkit.RegPathNoAdminRights
+ }
+ if (![System.String]::IsNullOrWhiteSpace($config.Toolkit.LogPathNoAdminRights))
+ {
+ $config.Toolkit.LogPath = $config.Toolkit.LogPathNoAdminRights
+ }
+ if (![System.String]::IsNullOrWhiteSpace($config.MSI.LogPathNoAdminRights))
+ {
+ $config.MSI.LogPath = $config.MSI.LogPathNoAdminRights
+ }
+ }
+
+ # Append the toolkit's name onto the temporary path.
+ $config.Toolkit.TempPath = [System.IO.Path]::Combine($config.Toolkit.TempPath, $adtEnv.appDeployToolkitName)
+
+ # Finally, return the config for usage within module.
+ return $config
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Import-ADTModuleDataFile
+#
+#-----------------------------------------------------------------------------
+
+function Import-ADTModuleDataFile
+{
+ [CmdletBinding()]
+ [OutputType([System.Collections.Hashtable])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName BaseDirectory -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (![System.IO.Directory]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName BaseDirectory -ProvidedValue $_ -ExceptionMessage 'The specified directory does not exist.'))
+ }
+ return $_
+ })]
+ [System.String[]]$BaseDirectory,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FileName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$UICulture,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IgnorePolicy
+ )
+
+ # Internal function to process the imported data.
+ function Update-ADTImportedDataValues
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [AllowEmptyCollection()]
+ [System.Collections.Hashtable]$DataFile,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Hashtable]$NewData
+ )
+
+ # Process the provided default data so we can add missing data to the data file.
+ foreach ($section in $NewData.GetEnumerator())
+ {
+ # Recursively process hashtables, otherwise just update the value.
+ if ($section.Value -is [System.Collections.Hashtable])
+ {
+ if (!$DataFile.ContainsKey($section.Key) -or ($DataFile.($section.Key) -isnot [System.Collections.Hashtable]))
+ {
+ $DataFile.($section.Key) = @{}
+ }
+ & $MyInvocation.MyCommand -DataFile $DataFile.($section.Key) -NewData $section.Value
+ }
+ elseif (!$DataFile.ContainsKey($section.Key) -or ![System.String]::IsNullOrWhiteSpace((& $Script:CommandTable.'Out-String' -InputObject $section.Value)))
+ {
+ $DataFile.($section.Key) = $section.Value
+ }
+ }
+ }
+
+ # Establish directory paths for the specified input.
+ $moduleDirectory = $Script:ADT.Directories.Defaults.([System.IO.Path]::GetFileNameWithoutExtension($FileName))
+ $callerDirectory = $BaseDirectory
+
+ # If we're running a release module, ensure the psd1 files haven't been tampered with.
+ if (($badFiles = & $Script:CommandTable.'Test-ADTReleaseBuildFileValidity' -LiteralPath $moduleDirectory))
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("The module's default $FileName file has been modified from its released state.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidData
+ ErrorId = 'ADTDataFileSignatureError'
+ TargetObject = $badFiles
+ RecommendedAction = "Please re-download $($MyInvocation.MyCommand.Module.Name) and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+
+ # Import the default data first and foremost.
+ $null = $PSBoundParameters.Remove('IgnorePolicy')
+ $PSBoundParameters.BaseDirectory = $moduleDirectory
+ $importedData = & $Script:CommandTable.'Import-LocalizedData' @PSBoundParameters
+
+ # Validate we imported something from our default location.
+ if (!$importedData.Count)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("The importation of the module's default $FileName file returned a null or empty result.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'ADTDataFileImportFailure'
+ TargetObject = [System.IO.Path]::Combine($PSBoundParameters.BaseDirectory, $FileName)
+ RecommendedAction = "Please ensure that this module is not corrupt or missing files, then try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+
+ # Super-impose the caller's data if it's different from default.
+ if (!$callerDirectory.Equals($moduleDirectory))
+ {
+ foreach ($directory in $callerDirectory)
+ {
+ $PSBoundParameters.BaseDirectory = $directory
+ Update-ADTImportedDataValues -DataFile $importedData -NewData (& $Script:CommandTable.'Import-LocalizedData' @PSBoundParameters)
+ }
+ }
+
+ # Super-impose registry values if they exist.
+ if (!$IgnorePolicy -and ($policySettings = & $Script:CommandTable.'Get-ChildItem' -LiteralPath "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\PSAppDeployToolkit\$([System.IO.Path]::GetFileNameWithoutExtension($FileName))" -ErrorAction Ignore | & $Script:CommandTable.'Convert-RegistryKeyToHashtable'))
+ {
+ Update-ADTImportedDataValues -DataFile $importedData -NewData $policySettings
+ }
+
+ # Return the built out data to the caller.
+ return $importedData
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Initialize-ADTClassicAssets
+#
+#-----------------------------------------------------------------------------
+
+function Initialize-ADTClassicAssets
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ # Return early if already initialised.
+ if ($Script:Dialogs.Classic.BannerHeight)
+ {
+ return
+ }
+
+ # Process the classic assets by grabbing the bytes of each image asset, storing them into a memory stream, then as an image for WinForms to use.
+ try
+ {
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ $Script:Dialogs.Classic.Assets.Logo = [System.Drawing.Image]::FromStream([System.IO.MemoryStream]::new([System.IO.File]::ReadAllBytes($adtConfig.Assets.Logo)))
+ $Script:Dialogs.Classic.Assets.Icon = [PSADT.Shared.Utility]::ConvertImageToIcon($Script:Dialogs.Classic.Assets.Logo)
+ $Script:Dialogs.Classic.Assets.Banner = [System.Drawing.Image]::FromStream([System.IO.MemoryStream]::new([System.IO.File]::ReadAllBytes($adtConfig.Assets.Banner)))
+ $Script:Dialogs.Classic.BannerHeight = [System.Math]::Ceiling($Script:Dialogs.Classic.Width * ($Script:Dialogs.Classic.Assets.Banner.Height / $Script:Dialogs.Classic.Assets.Banner.Width))
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Initialize-ADTModuleIfUnitialized
+#
+#-----------------------------------------------------------------------------
+
+function Initialize-ADTModuleIfUnitialized
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.PSCmdlet]$Cmdlet
+ )
+
+ # Initialize the module if there's no session and it hasn't been previously initialized.
+ if (!($adtSession = if (& $Script:CommandTable.'Test-ADTSessionActive') { & $Script:CommandTable.'Get-ADTSession' }) -and !(& $Script:CommandTable.'Test-ADTModuleInitialized'))
+ {
+ try
+ {
+ & $Script:CommandTable.'Initialize-ADTModule'
+ }
+ catch
+ {
+ $Cmdlet.ThrowTerminatingError($_)
+ }
+ }
+
+ # Return the current session if we happened to get one.
+ if ($adtSession)
+ {
+ return $adtSession
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTServiceAndDependencyOperation
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTServiceAndDependencyOperation
+{
+ <#
+
+ .SYNOPSIS
+ Process Windows service and its dependencies.
+
+ .DESCRIPTION
+ Process Windows service and its dependencies.
+
+ .PARAMETER Name
+ Specify the name of the service.
+
+ .PARAMETER SkipDependentServices
+ Choose to skip checking for dependent services.
+
+ .PARAMETER PendingStatusWait
+ The amount of time to wait for a service to get out of a pending state before continuing. Default is 60 seconds.
+
+ .PARAMETER PassThru
+ Return the System.ServiceProcess.ServiceController service object.
+
+ .INPUTS
+ None. You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.ServiceProcess.ServiceController. Returns the service object.
+
+ .EXAMPLE
+ Invoke-ADTServiceAndDependencyOperation -Name wuauserv -Operation Start
+
+ .EXAMPLE
+ Invoke-ADTServiceAndDependencyOperation -Name wuauserv -Operation Stop
+
+ .LINK
+ https://psappdeploytoolkit.com
+
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'SkipDependentServices', Justification = "This parameter is used within a child function that isn't immediately visible to PSScriptAnalyzer.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('Start', 'Stop')]
+ [System.String]$Operation,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipDependentServices,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$PendingStatusWait = [System.TimeSpan]::FromSeconds(60),
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ # Internal worker function.
+ function Invoke-ADTDependentServiceOperation
+ {
+ if (!$SkipDependentServices)
+ {
+ return
+ }
+
+ # Discover all dependent services.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Discovering all dependent service(s) for service [$Service] which are not '$(($status = if ($Operation -eq 'Start') {'Running'} else {'Stopped'}))'."
+ if (!($dependentServices = & $Script:CommandTable.'Get-Service' -Name $Service.ServiceName -DependentServices | & { process { if ($_.Status -ne $status) { return $_ } } }))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Dependent service(s) were not discovered for service [$Service]."
+ return
+ }
+
+ # Action each found dependent service.
+ foreach ($dependent in $dependentServices)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$(('Starting', 'Stopping')[$Operation -eq 'Start']) dependent service [$($dependent.ServiceName)] with display name [$($dependent.DisplayName)] and a status of [$($dependent.Status)]."
+ try
+ {
+ $dependent | & "$($Operation)-Service" -Force -WarningAction Ignore
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to $($Operation.ToLower()) dependent service [$($dependent.ServiceName)] with display name [$($dependent.DisplayName)] and a status of [$($dependent.Status)]. Continue..." -Severity 2
+ }
+ }
+ }
+
+ # Get the service object before continuing.
+ $Service = & $Script:CommandTable.'Get-Service' -Name $Name
+
+ # Wait up to 60 seconds if service is in a pending state.
+ if (($desiredStatus = @{ ContinuePending = 'Running'; PausePending = 'Paused'; StartPending = 'Running'; StopPending = 'Stopped' }.($Service.Status)))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Waiting for up to [$($PendingStatusWait.TotalSeconds)] seconds to allow service pending status [$($Service.Status)] to reach desired status [$([System.ServiceProcess.ServiceControllerStatus]$desiredStatus)]."
+ $Service.WaitForStatus($desiredStatus, $PendingStatusWait)
+ $Service.Refresh()
+ }
+
+ # Discover if the service is currently running.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Service [$($Service.ServiceName)] with display name [$($Service.DisplayName)] has a status of [$($Service.Status)]."
+ if (($Operation -eq 'Stop') -and ($Service.Status -ne 'Stopped'))
+ {
+ # Process all dependent services.
+ Invoke-ADTDependentServiceOperation
+
+ # Stop the parent service.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Stopping parent service [$($Service.ServiceName)] with display name [$($Service.DisplayName)]."
+ $Service = $Service | & $Script:CommandTable.'Stop-Service' -PassThru -WarningAction Ignore -Force
+ }
+ elseif (($Operation -eq 'Start') -and ($Service.Status -ne 'Running'))
+ {
+ # Start the parent service.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Starting parent service [$($Service.ServiceName)] with display name [$($Service.DisplayName)]."
+ $Service = $Service | & $Script:CommandTable.'Start-Service' -PassThru -WarningAction Ignore
+
+ # Process all dependent services.
+ Invoke-ADTDependentServiceOperation
+ }
+
+ # Return the service object if option selected.
+ if ($PassThru)
+ {
+ return $Service
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTSessionCallbackOperation
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTSessionCallbackOperation
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Action', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('Starting', 'Opening', 'Closing', 'Finishing')]
+ [System.String]$Type,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('Add', 'Remove')]
+ [System.String]$Action,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Cache the global callbacks and perform any required action.
+ $callbacks = $Script:ADT.Callbacks.$Type
+ $null = $Callback | & { process { if ($Action.Equals('Remove') -or !$callbacks.Contains($_)) { $callbacks.$Action($_) } } }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTSubstOperation
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTSubstOperation
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Create')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'Delete')]
+ [ValidateScript({
+ if ($_ -notmatch '^[A-Z]:$')
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Drive -ProvidedValue $_ -ExceptionMessage 'The specified drive is not valid. Please specify a drive in the following format: [A:, B:, etc].'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$Drive,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Create')]
+ [ValidateScript({
+ if ($null -eq $_)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified input is null.'))
+ }
+ if (!$_.Exists)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified image path cannot be found.'))
+ }
+ if ([System.Uri]::new($_).IsUnc)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified image path cannot be a network share.'))
+ }
+ return !!$_
+ })]
+ [System.IO.DirectoryInfo]$Path,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Delete')]
+ [System.Management.Automation.SwitchParameter]$Delete
+ )
+
+ # Perform the subst operation. An exit code of 0 is considered successful.
+ $substPath = "$([System.Environment]::SystemDirectory)\subst.exe"
+ $substResult = if ($Path)
+ {
+ # Throw if the specified drive letter is in use.
+ if ((& $Script:CommandTable.'Get-PSDrive' -PSProvider FileSystem).Name -contains $Drive.Substring(0, 1))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Drive -ProvidedValue $Drive -ExceptionMessage 'The specified drive is currently in use. Please try again with an unused drive letter.'))
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$(($msg = "Creating substitution drive [$Drive] for [$Path]"))."
+ & $substPath $Drive $Path.FullName
+ }
+ elseif ($Delete)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$(($msg = "Deleting substitution drive [$Drive]"))."
+ & $substPath $Drive /D
+ }
+ else
+ {
+ # If we're here, the caller probably did something silly like -Delete:$false.
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Unable to determine the required mode of operation.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'SubstModeIndeterminate'
+ TargetObject = $PSBoundParameters
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ if ($Global:LASTEXITCODE.Equals(0))
+ {
+ return
+ }
+
+ # If we're here, we had a bad exit code.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message ($msg = "$msg failed with exit code [$Global:LASTEXITCODE]: $substResult") -Severity 3
+ $naerParams = @{
+ Exception = [System.Runtime.InteropServices.ExternalException]::new($msg, $Global:LASTEXITCODE)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'SubstUtilityFailure'
+ TargetObject = $substResult
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTTerminalServerModeChange
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTTerminalServerModeChange
+{
+ <#
+
+ .SYNOPSIS
+ Changes the mode for Remote Desktop Session Host/Citrix servers.
+
+ .DESCRIPTION
+ Changes the mode for Remote Desktop Session Host/Citrix servers.
+
+ .INPUTS
+ None. You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None. This function does not return any objects.
+
+ .LINK
+ https://psappdeploytoolkit.com
+
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('Install', 'Execute')]
+ [System.String]$Mode
+ )
+
+ # Change the terminal server mode. An exit code of 1 is considered successful.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$(($msg = "Changing terminal server into user $($Mode.ToLower()) mode"))."
+ $terminalServerResult = & "$([System.Environment]::SystemDirectory)\change.exe" User /$Mode 2>&1
+ if ($Global:LASTEXITCODE.Equals(1))
+ {
+ return
+ }
+
+ # If we're here, we had a bad exit code.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message ($msg = "$msg failed with exit code [$Global:LASTEXITCODE]: $terminalServerResult") -Severity 3
+ $naerParams = @{
+ Exception = [System.Runtime.InteropServices.ExternalException]::new($msg, $Global:LASTEXITCODE)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'RdsChangeUtilityFailure'
+ TargetObject = $terminalServerResult
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTEnvironmentTable
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTEnvironmentTable
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This function does not change system state.")]
+ param
+ (
+ )
+
+ # Perform initial setup.
+ $variables = [ordered]@{}
+
+ ## Variables: Toolkit Info
+ $variables.Add('appDeployToolkitName', $MyInvocation.MyCommand.Module.Name)
+ $variables.Add('appDeployToolkitPath', $MyInvocation.MyCommand.Module.ModuleBase)
+ $variables.Add('appDeployMainScriptVersion', $MyInvocation.MyCommand.Module.Version)
+
+ ## Variables: Culture
+ $variables.Add('culture', $Host.CurrentCulture)
+ $variables.Add('uiculture', $Host.CurrentUICulture)
+ $variables.Add('currentLanguage', $variables.culture.TwoLetterISOLanguageName.ToUpper())
+ $variables.Add('currentUILanguage', $variables.uiculture.TwoLetterISOLanguageName.ToUpper())
+
+ ## Variables: Environment Variables
+ $variables.Add('envHost', $Host)
+ $variables.Add('envHostVersion', [System.Version]$Host.Version)
+ $variables.Add('envHostVersionSemantic', $(if ($Host.Version.PSObject.Properties.Name -match '^PSSemVer') { [System.Management.Automation.SemanticVersion]$Host.Version }))
+ $variables.Add('envHostVersionMajor', $variables.envHostVersion.Major)
+ $variables.Add('envHostVersionMinor', $variables.envHostVersion.Minor)
+ $variables.Add('envHostVersionBuild', $(if ($variables.envHostVersion.Build -ge 0) { $variables.envHostVersion.Build }))
+ $variables.Add('envHostVersionRevision', $(if ($variables.envHostVersion.Revision -ge 0) { $variables.envHostVersion.Revision }))
+ $variables.Add('envHostVersionPreReleaseLabel', $(if ($variables.envHostVersionSemantic -and $variables.envHostVersionSemantic.PreReleaseLabel) { $variables.envHostVersionSemantic.PreReleaseLabel }))
+ $variables.Add('envHostVersionBuildLabel', $(if ($variables.envHostVersionSemantic -and $variables.envHostVersionSemantic.BuildLabel) { $variables.envHostVersionSemantic.BuildLabel }))
+ $variables.Add('envAllUsersProfile', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonApplicationData))
+ $variables.Add('envAppData', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ApplicationData))
+ $variables.Add('envArchitecture', [System.Environment]::GetEnvironmentVariable('PROCESSOR_ARCHITECTURE'))
+ $variables.Add('envCommonDesktop', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonDesktopDirectory))
+ $variables.Add('envCommonDocuments', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonDocuments))
+ $variables.Add('envCommonStartMenuPrograms', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonPrograms))
+ $variables.Add('envCommonStartMenu', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonStartMenu))
+ $variables.Add('envCommonStartUp', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonStartup))
+ $variables.Add('envCommonTemplates', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonTemplates))
+ $variables.Add('envHomeDrive', [System.Environment]::GetEnvironmentVariable('HOMEDRIVE'))
+ $variables.Add('envHomePath', [System.Environment]::GetEnvironmentVariable('HOMEPATH'))
+ $variables.Add('envHomeShare', [System.Environment]::GetEnvironmentVariable('HOMESHARE'))
+ $variables.Add('envLocalAppData', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData))
+ $variables.Add('envLogicalDrives', [System.Environment]::GetLogicalDrives())
+ $variables.Add('envProgramData', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonApplicationData))
+ $variables.Add('envPublic', [System.Environment]::GetEnvironmentVariable('PUBLIC'))
+ $variables.Add('envSystemDrive', [System.IO.Path]::GetPathRoot([System.Environment]::SystemDirectory).TrimEnd('\'))
+ $variables.Add('envSystemRoot', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Windows))
+ $variables.Add('envTemp', [System.IO.Path]::GetTempPath())
+ $variables.Add('envUserCookies', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Cookies))
+ $variables.Add('envUserDesktop', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::DesktopDirectory))
+ $variables.Add('envUserFavorites', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Favorites))
+ $variables.Add('envUserInternetCache', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::InternetCache))
+ $variables.Add('envUserInternetHistory', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::History))
+ $variables.Add('envUserMyDocuments', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::MyDocuments))
+ $variables.Add('envUserName', [System.Environment]::UserName)
+ $variables.Add('envUserPictures', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::MyPictures))
+ $variables.Add('envUserProfile', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile))
+ $variables.Add('envUserSendTo', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::SendTo))
+ $variables.Add('envUserStartMenu', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::StartMenu))
+ $variables.Add('envUserStartMenuPrograms', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Programs))
+ $variables.Add('envUserStartUp', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::StartUp))
+ $variables.Add('envUserTemplates', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Templates))
+ $variables.Add('envSystem32Directory', [System.Environment]::SystemDirectory)
+ $variables.Add('envWinDir', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Windows))
+
+ ## Variables: Running in SCCM Task Sequence.
+ $variables.Add('RunningTaskSequence', !![System.Type]::GetTypeFromProgID('Microsoft.SMS.TSEnvironment'))
+
+ ## Variables: Domain Membership
+ $w32cs = & $Script:CommandTable.'Get-CimInstance' -ClassName Win32_ComputerSystem -Verbose:$false
+ $w32csd = $w32cs.Domain | & { process { if ($_) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1
+ $variables.Add('IsMachinePartOfDomain', $w32cs.PartOfDomain)
+ $variables.Add('envMachineWorkgroup', $null)
+ $variables.Add('envMachineADDomain', $null)
+ $variables.Add('envLogonServer', $null)
+ $variables.Add('MachineDomainController', $null)
+ $variables.Add('envMachineDNSDomain', ([System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().DomainName | & { process { if ($_) { return $_.ToLower() } } } | & $Script:CommandTable.'Select-Object' -First 1))
+ $variables.Add('envUserDNSDomain', ([System.Environment]::GetEnvironmentVariable('USERDNSDOMAIN') | & { process { if ($_) { return $_.ToLower() } } } | & $Script:CommandTable.'Select-Object' -First 1))
+ $variables.Add('envUserDomain', $(if ([System.Environment]::UserDomainName) { [System.Environment]::UserDomainName.ToUpper() }))
+ $variables.Add('envComputerName', $w32cs.DNSHostName.ToUpper())
+ $variables.Add('envComputerNameFQDN', $variables.envComputerName)
+ if ($variables.IsMachinePartOfDomain)
+ {
+ $variables.envMachineADDomain = $w32csd.ToLower()
+ $variables.envComputerNameFQDN = try
+ {
+ [System.Net.Dns]::GetHostEntry('localhost').HostName
+ }
+ catch
+ {
+ # Function GetHostEntry failed, but we can construct the FQDN in another way
+ $variables.envComputerNameFQDN + '.' + $variables.envMachineADDomain
+ }
+
+ # Set the logon server and remove backslashes at the beginning.
+ $variables.envLogonServer = $(try
+ {
+ [System.Environment]::GetEnvironmentVariable('LOGONSERVER') | & { process { if ($_ -and !$_.Contains('\\MicrosoftAccount')) { [System.Net.Dns]::GetHostEntry($_.TrimStart('\')).HostName } } }
+ }
+ catch
+ {
+ # If running in system context or if GetHostEntry fails, fall back on the logonserver value stored in the registry
+ & $Script:CommandTable.'Get-ItemProperty' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\History' -ErrorAction Ignore | & $Script:CommandTable.'Select-Object' -ExpandProperty DCName -ErrorAction Ignore
+ })
+ while ($variables.envLogonServer -and $variables.envLogonServer.StartsWith('\'))
+ {
+ $variables.envLogonServer = $variables.envLogonServer.Substring(1)
+ }
+
+ try
+ {
+ $variables.MachineDomainController = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().FindDomainController().Name
+ }
+ catch
+ {
+ $null = $null
+ }
+ }
+ else
+ {
+ $variables.envMachineWorkgroup = $w32csd.ToUpper()
+ }
+
+ # Get the OS Architecture.
+ $variables.Add('Is64Bit', [System.Environment]::Is64BitOperatingSystem)
+ $variables.Add('envOSArchitecture', [PSADT.OperatingSystem.OSHelper]::GetArchitecture())
+
+ ## Variables: Current Process Architecture
+ $variables.Add('Is64BitProcess', [System.Environment]::Is64BitProcess)
+ $variables.Add('psArchitecture', (& $Script:CommandTable.'Get-ADTPEFileArchitecture' -FilePath ([System.Diagnostics.Process]::GetCurrentProcess().Path)))
+
+ ## Variables: Get normalized paths that vary depending on process bitness.
+ if ($variables.Is64Bit)
+ {
+ if ($variables.Is64BitProcess)
+ {
+ $variables.Add('envProgramFiles', [System.Environment]::GetFolderPath('ProgramFiles'))
+ $variables.Add('envCommonProgramFiles', [System.Environment]::GetFolderPath('CommonProgramFiles'))
+ $variables.Add('envSysNativeDirectory', [System.Environment]::SystemDirectory)
+ $variables.Add('envSYSWOW64Directory', [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::SystemX86))
+ }
+ else
+ {
+ $variables.Add('envProgramFiles', [System.Environment]::GetEnvironmentVariable('ProgramW6432'))
+ $variables.Add('envCommonProgramFiles', [System.Environment]::GetEnvironmentVariable('CommonProgramW6432'))
+ $variables.Add('envSysNativeDirectory', [System.IO.Path]::Combine([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Windows), 'sysnative'))
+ $variables.Add('envSYSWOW64Directory', [System.Environment]::SystemDirectory)
+ }
+ $variables.Add('envProgramFilesX86', [System.Environment]::GetFolderPath('ProgramFilesX86'))
+ $variables.Add('envCommonProgramFilesX86', [System.Environment]::GetFolderPath('CommonProgramFilesX86'))
+ }
+ else
+ {
+ $variables.Add('envProgramFiles', [System.Environment]::GetFolderPath('ProgramFiles'))
+ $variables.Add('envProgramFilesX86', $null)
+ $variables.Add('envCommonProgramFiles', [System.Environment]::GetFolderPath('CommonProgramFiles'))
+ $variables.Add('envCommonProgramFilesX86', $null)
+ $variables.Add('envSysNativeDirectory', [System.Environment]::SystemDirectory)
+ $variables.Add('envSYSWOW64Directory', $null)
+ }
+
+ ## Variables: Operating System
+ $variables.Add('envOS', (& $Script:CommandTable.'Get-CimInstance' -ClassName Win32_OperatingSystem -Verbose:$false))
+ $variables.Add('envOSName', $variables.envOS.Caption.Trim())
+ $variables.Add('envOSServicePack', $variables.envOS.CSDVersion)
+ $variables.Add('envOSVersion', [version][System.Diagnostics.FileVersionInfo]::GetVersionInfo([System.IO.Path]::Combine($variables.envSysNativeDirectory, 'ntoskrnl.exe')).ProductVersion)
+ $variables.Add('envOSVersionMajor', $variables.envOSVersion.Major)
+ $variables.Add('envOSVersionMinor', $variables.envOSVersion.Minor)
+ $variables.Add('envOSVersionBuild', $(if ($variables.envOSVersion.Build -ge 0) { $variables.envOSVersion.Build }))
+ $variables.Add('envOSVersionRevision', $(if ($variables.envOSVersion.Revision -ge 0) { $variables.envOSVersion.Revision }))
+
+ # Get the operating system type.
+ $variables.Add('envOSProductType', $variables.envOS.ProductType)
+ $variables.Add('IsServerOS', $variables.envOSProductType -eq 3)
+ $variables.Add('IsDomainControllerOS', $variables.envOSProductType -eq 2)
+ $variables.Add('IsWorkstationOS', $variables.envOSProductType -eq 1)
+ $variables.Add('IsMultiSessionOS', (& $Script:CommandTable.'Test-ADTIsMultiSessionOS'))
+ $variables.Add('envOSProductTypeName', $(switch ($variables.envOSProductType)
+ {
+ 3 { 'Server'; break }
+ 2 { 'Domain Controller'; break }
+ 1 { 'Workstation'; break }
+ default { 'Unknown'; break }
+ }))
+
+ ## Variables: Office C2R version, bitness and channel
+ $variables.Add('envOfficeVars', (& $Script:CommandTable.'Get-ItemProperty' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\ClickToRun\Configuration' -ErrorAction Ignore))
+ $variables.Add('envOfficeVersion', ($variables.envOfficeVars | & $Script:CommandTable.'Select-Object' -ExpandProperty VersionToReport -ErrorAction Ignore))
+ $variables.Add('envOfficeBitness', ($variables.envOfficeVars | & $Script:CommandTable.'Select-Object' -ExpandProperty Platform -ErrorAction Ignore))
+
+ # Channel needs special handling for group policy values.
+ $officeChannelProperty = if ($variables.envOfficeVars | & $Script:CommandTable.'Select-Object' -ExpandProperty UpdateChannel -ErrorAction Ignore)
+ {
+ $variables.envOfficeVars.UpdateChannel
+ }
+ elseif ($variables.envOfficeVars | & $Script:CommandTable.'Select-Object' -ExpandProperty CDNBaseURL -ErrorAction Ignore)
+ {
+ $variables.envOfficeVars.CDNBaseURL
+ }
+ $variables.Add('envOfficeChannel', $(switch ($officeChannelProperty -replace '^.+/')
+ {
+ "492350f6-3a01-4f97-b9c0-c7c6ddf67d60" { "monthly"; break }
+ "7ffbc6bf-bc32-4f92-8982-f9dd17fd3114" { "semi-annual"; break }
+ "64256afe-f5d9-4f86-8936-8840a6a4f5be" { "monthly targeted"; break }
+ "b8f9b850-328d-4355-9145-c59439a0c4cf" { "semi-annual targeted"; break }
+ "55336b82-a18d-4dd6-b5f6-9e5095c314a6" { "monthly enterprise"; break }
+ }))
+
+ ## Variables: Hardware
+ $w32b = & $Script:CommandTable.'Get-CimInstance' -ClassName Win32_BIOS -Verbose:$false
+ $variables.Add('envSystemRAM', [System.Math]::Round($w32cs.TotalPhysicalMemory / 1GB))
+ $variables.Add('envHardwareType', $(if (($w32b.Version -match 'VRTUAL') -or (($w32cs.Manufacturer -like '*Microsoft*') -and ($w32cs.Model -notlike '*Surface*')))
+ {
+ 'Virtual:Hyper-V'
+ }
+ elseif ($w32b.Version -match 'A M I')
+ {
+ 'Virtual:Virtual PC'
+ }
+ elseif ($w32b.Version -like '*Xen*')
+ {
+ 'Virtual:Xen'
+ }
+ elseif (($w32b.SerialNumber -like '*VMware*') -or ($w32cs.Manufacturer -like '*VMWare*'))
+ {
+ 'Virtual:VMware'
+ }
+ elseif (($w32b.SerialNumber -like '*Parallels*') -or ($w32cs.Manufacturer -like '*Parallels*'))
+ {
+ 'Virtual:Parallels'
+ }
+ elseif ($w32cs.Model -like '*Virtual*')
+ {
+ 'Virtual'
+ }
+ else
+ {
+ 'Physical'
+ }))
+
+ ## Variables: PowerShell And CLR (.NET) Versions
+ $variables.Add('envPSVersionTable', $PSVersionTable)
+ $variables.Add('envPSProcessPath', (& $Script:CommandTable.'Get-ADTPowerShellProcessPath'))
+
+ # PowerShell Version
+ $variables.Add('envPSVersion', [System.Version]$variables.envPSVersionTable.PSVersion)
+ $variables.Add('envPSVersionSemantic', $(if ($variables.envPSVersionTable.PSVersion.GetType().FullName.Equals('System.Management.Automation.SemanticVersion')) { $variables.envPSVersionTable.PSVersion }))
+ $variables.Add('envPSVersionMajor', $variables.envPSVersion.Major)
+ $variables.Add('envPSVersionMinor', $variables.envPSVersion.Minor)
+ $variables.Add('envPSVersionBuild', $(if ($variables.envPSVersion.Build -ge 0) { $variables.envPSVersion.Build }))
+ $variables.Add('envPSVersionRevision', $(if ($variables.envPSVersion.Revision -ge 0) { $variables.envPSVersion.Revision }))
+ $variables.Add('envPSVersionPreReleaseLabel', $(if ($variables.envPSVersionSemantic -and $variables.envPSVersionSemantic.PreReleaseLabel) { $variables.envPSVersionSemantic.PreReleaseLabel }))
+ $variables.Add('envPSVersionBuildLabel', $(if ($variables.envPSVersionSemantic -and $variables.envPSVersionSemantic.BuildLabel) { $variables.envPSVersionSemantic.BuildLabel }))
+
+ # CLR (.NET) Version used by Windows PowerShell
+ if ($variables.envPSVersionTable.ContainsKey('CLRVersion'))
+ {
+ $variables.Add('envCLRVersion', $variables.envPSVersionTable.CLRVersion)
+ $variables.Add('envCLRVersionMajor', $variables.envCLRVersion.Major)
+ $variables.Add('envCLRVersionMinor', $variables.envCLRVersion.Minor)
+ $variables.Add('envCLRVersionBuild', $(if ($variables.envCLRVersion.Build -ge 0) { $variables.envCLRVersion.Build }))
+ $variables.Add('envCLRVersionRevision', $(if ($variables.envCLRVersion.Revision -ge 0) { $variables.envCLRVersion.Revision }))
+ }
+ else
+ {
+ $variables.Add('envCLRVersion', $null)
+ $variables.Add('envCLRVersionMajor', $null)
+ $variables.Add('envCLRVersionMinor', $null)
+ $variables.Add('envCLRVersionBuild', $null)
+ $variables.Add('envCLRVersionRevision', $null)
+ }
+
+ ## Variables: Permissions/Accounts
+ $variables.Add('CurrentProcessToken', [System.Security.Principal.WindowsIdentity]::GetCurrent())
+ $variables.Add('CurrentProcessSID', [System.Security.Principal.SecurityIdentifier]$variables.CurrentProcessToken.User)
+ $variables.Add('ProcessNTAccount', $variables.CurrentProcessToken.Name)
+ $variables.Add('ProcessNTAccountSID', $variables.CurrentProcessSID.Value)
+ $variables.Add('IsAdmin', (& $Script:CommandTable.'Test-ADTCallerIsAdmin'))
+ $variables.Add('IsLocalSystemAccount', $variables.CurrentProcessSID.IsWellKnown([System.Security.Principal.WellKnownSidType]::LocalSystemSid))
+ $variables.Add('IsLocalServiceAccount', $variables.CurrentProcessSID.IsWellKnown([System.Security.Principal.WellKnownSidType]::LocalServiceSid))
+ $variables.Add('IsNetworkServiceAccount', $variables.CurrentProcessSID.IsWellKnown([System.Security.Principal.WellKnownSidType]::NetworkServiceSid))
+ $variables.Add('IsServiceAccount', ($variables.CurrentProcessToken.Groups -contains ([System.Security.Principal.SecurityIdentifier]'S-1-5-6')))
+ $variables.Add('IsProcessUserInteractive', [System.Environment]::UserInteractive)
+ $variables.Add('LocalSystemNTAccount', (& $Script:CommandTable.'ConvertTo-ADTNTAccountOrSID' -WellKnownSIDName LocalSystemSid -WellKnownToNTAccount -LocalHost 4>$null).Value)
+ $variables.Add('LocalUsersGroup', (& $Script:CommandTable.'ConvertTo-ADTNTAccountOrSID' -WellKnownSIDName BuiltinUsersSid -WellKnownToNTAccount -LocalHost 4>$null).Value)
+ $variables.Add('LocalAdministratorsGroup', (& $Script:CommandTable.'ConvertTo-ADTNTAccountOrSID' -WellKnownSIDName BuiltinAdministratorsSid -WellKnownToNTAccount -LocalHost 4>$null).Value)
+ $variables.Add('SessionZero', $variables.IsLocalSystemAccount -or $variables.IsLocalServiceAccount -or $variables.IsNetworkServiceAccount -or $variables.IsServiceAccount)
+
+ ## Variables: Logged on user information
+ $variables.Add('LoggedOnUserSessions', (& $Script:CommandTable.'Get-ADTLoggedOnUser'))
+ $variables.Add('usersLoggedOn', ($variables.LoggedOnUserSessions | & { process { if ($_) { $_.NTAccount } } }))
+ $variables.Add('CurrentLoggedOnUserSession', ($variables.LoggedOnUserSessions | & { process { if ($_ -and $_.IsCurrentSession) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1))
+ $variables.Add('CurrentConsoleUserSession', ($variables.LoggedOnUserSessions | & { process { if ($_ -and $_.IsConsoleSession) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1))
+ $variables.Add('RunAsActiveUser', $(if ($null -ne $variables.LoggedOnUserSessions) { & $Script:CommandTable.'Get-ADTRunAsActiveUser' -UserSessionInfo $variables.LoggedOnUserSessions }))
+
+ ## Variables: User profile information.
+ $variables.Add('dirUserProfile', [System.IO.Directory]::GetParent($variables.envPublic))
+ $variables.Add('userProfileName', $(if ($variables.RunAsActiveUser) { $variables.RunAsActiveUser.UserName }))
+ $variables.Add('runasUserProfile', $(if ($variables.userProfileName) { & $Script:CommandTable.'Join-Path' -Path $variables.dirUserProfile -ChildPath $variables.userProfileName -Resolve -ErrorAction Ignore }))
+
+ ## Variables: Invalid FileName Characters
+ $variables.Add('invalidFileNameChars', [System.IO.Path]::GetInvalidFileNameChars())
+ $variables.Add('invalidFileNameCharsRegExPattern', "[$([System.Text.RegularExpressions.Regex]::Escape([System.String]::Join($null, $variables.invalidFileNameChars)))]")
+
+ ## Variables: RegEx Patterns
+ $variables.Add('MSIProductCodeRegExPattern', '^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$')
+ $variables.Add('InvalidScheduledTaskNameCharsRegExPattern', "[$([System.Text.RegularExpressions.Regex]::Escape('\/:*?"<>|'))]")
+
+ # Add in WScript shell variables.
+ $variables.Add('Shell', [System.Activator]::CreateInstance([System.Type]::GetTypeFromProgID('WScript.Shell')))
+ $variables.Add('ShellApp', [System.Activator]::CreateInstance([System.Type]::GetTypeFromProgID('Shell.Application')))
+
+ # Return variables for use within the module.
+ return $variables.AsReadOnly()
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTPreferenceVariables
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTPreferenceVariables
+{
+ <#
+ .SYNOPSIS
+ Sets preference variables within the called scope based on CommonParameter values within the callstack.
+
+ .DESCRIPTION
+ Script module functions do not automatically inherit their caller's variables, therefore we walk the callstack to get the closest bound CommonParameter value and use it within the called scope.
+
+ This function is a helper function for any script module Advanced Function; by passing in the values of $ExecutionContext.SessionState, Set-ADTPreferenceVariables will set the caller's preference variables locally.
+
+ .PARAMETER SessionState
+ The $ExecutionContext.SessionState object from a script module Advanced Function. This is how the Set-ADTPreferenceVariables function sets variables in its callers' scope, even if that caller is in a different script module.
+
+ .PARAMETER Scope
+ A scope override, mostly so this can be called via Initialize-ADTFunction.
+
+ .EXAMPLE
+ Set-ADTPreferenceVariables -SessionState $ExecutionContext.SessionState
+
+ Imports the default PowerShell preference variables from the caller into the local scope.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Original code inspired by: https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This compatibility wrapper function cannot have its name changed for backwards compatiblity purposes.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SessionState]$SessionState,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$Scope = 1
+ )
+
+ # Get the callstack so we can enumerate bound parameters of our callers.
+ $stackParams = (& $Script:CommandTable.'Get-PSCallStack').InvocationInfo.BoundParameters.GetEnumerator().GetEnumerator()
+
+ # Loop through each common parameter and get the first bound value.
+ foreach ($pref in $Script:PreferenceVariableTable.GetEnumerator())
+ {
+ # Return early if we have nothing.
+ if (!($param = $stackParams | & { process { if ($_.Key.Equals($pref.Key)) { return @{ Name = $pref.Value; Value = $_.Value } } } } | & $Script:CommandTable.'Select-Object' -First 1))
+ {
+ continue
+ }
+
+ # If we've hit a switch, default it to an ActionPreference of Continue.
+ if ($param.Value -is [System.Management.Automation.SwitchParameter])
+ {
+ if (!$param.Value)
+ {
+ continue
+ }
+ $param.Value = [System.Management.Automation.ActionPreference]::Continue
+ }
+
+ # When we're within the same module, just go up a scope level to set the value.
+ # If the caller in an external scope, we set this within their SessionState.
+ if ($SessionState.Equals($ExecutionContext.SessionState))
+ {
+ & $Script:CommandTable.'Set-Variable' @param -Scope $Scope -Force -Confirm:$false -WhatIf:$false
+ }
+ else
+ {
+ $SessionState.PSVariable.Set($param.Value, $param.Value)
+ }
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTHelpConsoleInternal
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTHelpConsoleInternal
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ModuleName,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Guid]$Guid,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Version]$ModuleVersion
+ )
+
+ # Ensure script runs in strict mode since this may be called in a new scope.
+ $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
+ $ProgressPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
+ Set-StrictMode -Version 3
+
+ # Import the module and store its passthru data so we can access it later.
+ $module = Import-Module -FullyQualifiedName ([Microsoft.PowerShell.Commands.ModuleSpecification]::new($PSBoundParameters)) -PassThru
+
+ # Build out the form's listbox.
+ $helpListBox = [System.Windows.Forms.ListBox]::new()
+ $helpListBox.ClientSize = [System.Drawing.Size]::new(261, 675)
+ $helpListBox.Font = [System.Drawing.SystemFonts]::MessageBoxFont
+ $helpListBox.Location = [System.Drawing.Point]::new(3, 0)
+ $helpListBox.add_SelectedIndexChanged({ $helpTextBox.Text = [System.String]::Join("`n", ((Get-Help -Name $helpListBox.SelectedItem -Full | Out-String -Stream -Width ([System.Int32]::MaxValue)) -replace '^\s+$').TrimEnd()).Trim().Replace('
', $null) })
+ $null = $helpListBox.Items.AddRange(($module.ExportedCommands.Keys | Sort-Object))
+
+ # Build out the form's textbox.
+ $helpTextBox = [System.Windows.Forms.RichTextBox]::new()
+ $helpTextBox.ClientSize = [System.Drawing.Size]::new(1250, 675)
+ $helpTextBox.Font = [System.Drawing.Font]::new('Consolas', 9)
+ $helpTextBox.Location = [System.Drawing.Point]::new(271, 0)
+ $helpTextBox.ReadOnly = $true
+ $helpTextBox.WordWrap = $false
+
+ # Build out the form. The suspend/resume is crucial for HiDPI support!
+ $helpForm = [System.Windows.Forms.Form]::new()
+ $helpForm.SuspendLayout()
+ $helpForm.Text = "$($module.Name) Help Console"
+ $helpForm.Font = [System.Drawing.SystemFonts]::MessageBoxFont
+ $helpForm.AutoScaleDimensions = [System.Drawing.SizeF]::new(7, 15)
+ $helpForm.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
+ $helpForm.AutoSize = $true
+ $helpForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D
+ $helpForm.MaximizeBox = $false
+ $helpForm.Controls.Add($helpListBox)
+ $helpForm.Controls.Add($helpTextBox)
+ $helpForm.ResumeLayout()
+
+ # Show the form. Using Application.Run automatically manages disposal for us.
+ [System.Windows.Forms.Application]::Run($helpForm)
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationProgressClassic
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationProgressClassic
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'NoRelocation', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WindowTitle,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$StatusMessage,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Default', 'TopLeft', 'Top', 'TopRight', 'TopCenter', 'BottomLeft', 'Bottom', 'BottomRight')]
+ [System.String]$WindowLocation = 'Default',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Windows.TextAlignment]$MessageAlignment = [System.Windows.TextAlignment]::Center,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoRelocation,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Internal worker function.
+ function Update-WindowLocation
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'This is an internal worker function that requires no end user confirmation.')]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Windows.Window]$Window,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Default', 'TopLeft', 'Top', 'TopRight', 'TopCenter', 'BottomLeft', 'Bottom', 'BottomRight')]
+ [System.String]$Location = 'Default'
+ )
+
+ # Calculate the position on the screen where the progress dialog should be placed.
+ [System.Double]$screenCenterWidth = [System.Windows.SystemParameters]::WorkArea.Width - $Window.ActualWidth
+ [System.Double]$screenCenterHeight = [System.Windows.SystemParameters]::WorkArea.Height - $Window.ActualHeight
+
+ # Set the start position of the Window based on the screen size.
+ switch ($Location)
+ {
+ 'TopLeft'
+ {
+ $Window.Left = 0.
+ $Window.Top = 0.
+ break
+ }
+ 'Top'
+ {
+ $Window.Left = $screenCenterWidth * 0.5
+ $Window.Top = 0.
+ break
+ }
+ 'TopRight'
+ {
+ $Window.Left = $screenCenterWidth
+ $Window.Top = 0.
+ break
+ }
+ 'TopCenter'
+ {
+ $Window.Left = $screenCenterWidth * 0.5
+ $Window.Top = $screenCenterHeight * (1. / 6.)
+ break
+ }
+ 'BottomLeft'
+ {
+ $Window.Left = 0.
+ $Window.Top = $screenCenterHeight
+ break
+ }
+ 'Bottom'
+ {
+ $Window.Left = $screenCenterWidth * 0.5
+ $Window.Top = $screenCenterHeight
+ break
+ }
+ 'BottomRight'
+ {
+ # The -100 offset is needed to not overlap system tray toast notifications.
+ $Window.Left = $screenCenterWidth
+ $Window.Top = $screenCenterHeight - 100
+ break
+ }
+ default
+ {
+ # Center the progress window by calculating the center of the workable screen based on the width of the screen minus half the width of the progress bar
+ $Window.Left = $screenCenterWidth * 0.5
+ $Window.Top = $screenCenterHeight * 0.5
+ break
+ }
+ }
+ }
+
+ # Check if the progress thread is running before invoking methods on it.
+ if (!$Script:Dialogs.Classic.ProgressWindow.Running)
+ {
+ # Load up the XML file.
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ $xaml = [System.Xml.XmlDocument]::new()
+ $xaml.Load($Script:Dialogs.Classic.ProgressWindow.XamlCode)
+ $xaml.Window.Title = $xaml.Window.ToolTip = $WindowTitle
+ $xaml.Window.TopMost = (!$NotTopMost).ToString()
+ $xaml.Window.Grid.TextBlock.Text = $StatusMessage
+ $xaml.Window.Grid.TextBlock.TextAlignment = $MessageAlignment.ToString()
+
+ # Set up the PowerShell instance and commence invocation.
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell = [System.Management.Automation.PowerShell]::Create().AddScript($Script:CommandTable.'Show-ADTInstallationProgressClassicInternal'.ScriptBlock).AddArgument($Xaml).AddArgument($adtConfig.Assets.Logo).AddArgument($adtConfig.Assets.Banner).AddArgument($WindowLocation).AddArgument(${Function:Update-WindowLocation}.Ast.Body.GetScriptBlock()).AddArgument($Script:CommandTable.'Disable-ADTWindowCloseButton'.ScriptBlock.Ast.Body.GetScriptBlock())
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.ApartmentState = [System.Threading.ApartmentState]::STA
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.ThreadOptions = [System.Management.Automation.Runspaces.PSThreadOptions]::ReuseThread
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.Open()
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.SessionStateProxy.SetVariable('SyncHash', $Script:Dialogs.Classic.ProgressWindow.SyncHash)
+ $Script:Dialogs.Classic.ProgressWindow.Invocation = $Script:Dialogs.Classic.ProgressWindow.PowerShell.BeginInvoke()
+
+ # Allow the thread to be spun up safely before invoking actions against it.
+ while (!($Script:Dialogs.Classic.ProgressWindow.SyncHash.ContainsKey('Window') -and $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window.IsInitialized -and $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window.Dispatcher.Thread.ThreadState.Equals([System.Threading.ThreadState]::Running)))
+ {
+ if ($Script:Dialogs.Classic.ProgressWindow.Invocation.IsCompleted)
+ {
+ if (!$Script:Dialogs.Classic.ProgressWindow.PowerShell.HadErrors)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("The separate thread completed without presenting the progress dialog.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'InstallationProgressDialogFailure'
+ TargetObject = $(if ($Script:Dialogs.Classic.ProgressWindow.SyncHash.ContainsKey('Window')) { $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window })
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ $Script:Dialogs.Classic.ProgressWindow.PowerShell.Runspace.SessionStateProxy.PSVariable.GetValue('Error') | & { process { if ($_ -is [System.Management.Automation.ErrorRecord]) { $PSCmdlet.ThrowTerminatingError($_) } } }
+ }
+ }
+
+ # If we're here, the window came up.
+ $Script:Dialogs.Classic.ProgressWindow.Running = $true
+ }
+ else
+ {
+ # Invoke update events against an established window.
+ $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window.Dispatcher.Invoke(
+ {
+ $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window.Title = $WindowTitle
+ $Script:Dialogs.Classic.ProgressWindow.SyncHash.Message.Text = $StatusMessage
+ $Script:Dialogs.Classic.ProgressWindow.SyncHash.Message.TextAlignment = $MessageAlignment
+ if (!$NoRelocation)
+ {
+ Update-WindowLocation -Window $Script:Dialogs.Classic.ProgressWindow.SyncHash.Window -Location $WindowLocation
+ }
+ },
+ [System.Windows.Threading.DispatcherPriority]::Send
+ )
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationProgressClassicInternal
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationProgressClassicInternal
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'DisableWindowCloseButton', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UpdateWindowLocation', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'WindowLocation', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Xml.XmlDocument]$Xaml,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.IO.FileInfo]$Icon,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.IO.FileInfo]$Banner,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('Default', 'TopLeft', 'Top', 'TopRight', 'TopCenter', 'BottomLeft', 'Bottom', 'BottomRight')]
+ [System.String]$WindowLocation,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ScriptBlock]$UpdateWindowLocation,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ScriptBlock]$DisableWindowCloseButton
+ )
+
+ # Set required variables to ensure script functionality.
+ $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
+ $ProgressPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
+ Set-StrictMode -Version 3
+
+ # Create XAML window and bring it up.
+ try
+ {
+ $SyncHash.Add('Window', [System.Windows.Markup.XamlReader]::Load([System.Xml.XmlNodeReader]::new($Xaml)))
+ $SyncHash.Add('Message', $SyncHash.Window.FindName('ProgressText'))
+ $SyncHash.Window.Icon = [System.Windows.Media.Imaging.BitmapFrame]::Create([System.IO.MemoryStream]::new([System.IO.File]::ReadAllBytes($Icon)), [System.Windows.Media.Imaging.BitmapCreateOptions]::IgnoreImageCache, [System.Windows.Media.Imaging.BitmapCacheOption]::OnLoad)
+ $SyncHash.Window.FindName('ProgressBanner').Source = [System.Windows.Media.Imaging.BitmapFrame]::Create([System.IO.MemoryStream]::new([System.IO.File]::ReadAllBytes($Banner)), [System.Windows.Media.Imaging.BitmapCreateOptions]::IgnoreImageCache, [System.Windows.Media.Imaging.BitmapCacheOption]::OnLoad)
+ $SyncHash.Window.add_MouseLeftButtonDown({ $this.DragMove() })
+ $SyncHash.Window.add_Loaded({
+ # Relocate the window and disable the X button.
+ & $UpdateWindowLocation -Window $this -Location $WindowLocation
+ & $DisableWindowCloseButton -WindowHandle ([System.Windows.Interop.WindowInteropHelper]::new($this).Handle)
+ })
+ $null = $SyncHash.Window.ShowDialog()
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationProgressFluent
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationProgressFluent
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WindowTitle,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WindowSubtitle,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$StatusMessage,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$StatusMessageDetail,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Perform initial setup.
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Advise that repositioning the progress window is unsupported for fluent.
+ if ($UnboundArguments -eq '-WindowLocation:')
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The parameter [-WindowLocation] is not supported with fluent dialogs and has no effect." -Severity 2
+ }
+
+ # Check if the progress thread is running before invoking methods on it.
+ if (!$Script:Dialogs.Fluent.ProgressWindow.Running)
+ {
+ # Instantiate a new progress window object and start it up.
+ [PSADT.UserInterface.UnifiedADTApplication]::ShowProgressDialog(
+ $WindowTitle,
+ $WindowSubtitle,
+ !$NotTopMost,
+ $adtConfig.Assets.Logo,
+ $StatusMessage,
+ $StatusMessageDetail
+ )
+
+ # Allow the thread to be spun up safely before invoking actions against it.
+ do
+ {
+ $Script:Dialogs.Fluent.ProgressWindow.Running = [PSADT.UserInterface.UnifiedADTApplication]::CurrentDialogVisible()
+ }
+ until ($Script:Dialogs.Fluent.ProgressWindow.Running)
+ }
+ else
+ {
+ # Update all values.
+ [PSADT.UserInterface.UnifiedADTApplication]::UpdateProgress($null, $StatusMessage, $StatusMessageDetail)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationPromptClassic
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationPromptClassic
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'formInstallationPromptStartLocation', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Message,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Left', 'Center', 'Right')]
+ [System.String]$MessageAlignment = 'Center',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonRightText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonLeftText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonMiddleText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Application', 'Asterisk', 'Error', 'Exclamation', 'Hand', 'Information', 'Question', 'Shield', 'Warning', 'WinLogo')]
+ [System.String]$Icon,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PersistPrompt,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$MinimizeWindows,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$Timeout,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoExitOnTimeout,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Set up some default values.
+ $controlSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, 0)
+ $paddingNone = [System.Windows.Forms.Padding]::new(0, 0, 0, 0)
+ $buttonSize = [System.Drawing.Size]::new(130, 24)
+ $adtEnv = & $Script:CommandTable.'Get-ADTEnvironmentTable'
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Initalise the classic assets.
+ & $Script:CommandTable.'Initialize-ADTClassicAssets'
+
+ # Define events for form windows.
+ $installPromptTimer_Tick = {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Installation action not taken within a reasonable amount of time.'
+ $buttonAbort.PerformClick()
+ }
+ $installPromptTimerPersist_Tick = {
+ $formInstallationPrompt.WindowState = [System.Windows.Forms.FormWindowState]::Normal
+ $formInstallationPrompt.TopMost = !$NotTopMost
+ $formInstallationPrompt.Location = $formInstallationPromptStartLocation
+ $formInstallationPrompt.BringToFront()
+ }
+ $formInstallationPrompt_FormClosed = {
+ # Remove all event handlers from the controls.
+ $installPromptTimer.remove_Tick($installPromptTimer_Tick)
+ $installPromptTimer.Dispose()
+ $installPromptTimer = $null
+ $installPromptTimerPersist.remove_Tick($installPromptTimerPersist_Tick)
+ $installPromptTimerPersist.Dispose()
+ $installPromptTimerPersist = $null
+ $formInstallationPrompt.remove_Load($formInstallationPrompt_Load)
+ $formInstallationPrompt.remove_FormClosed($formInstallationPrompt_FormClosed)
+ $formInstallationPrompt.Dispose()
+ $formInstallationPrompt = $null
+ }
+ $formInstallationPrompt_Load = {
+ # Disable the X button.
+ try
+ {
+ & $Script:CommandTable.'Disable-ADTWindowCloseButton' -WindowHandle $formInstallationPrompt.Handle
+ }
+ catch
+ {
+ # Not a terminating error if we can't disable the button. Just disable the Control Box instead.
+ & $Script:CommandTable.'Write-ADTLogEntry' 'Failed to disable the Close button. Disabling the Control Box instead.' -Severity 2
+ $formInstallationPrompt.ControlBox = $false
+ }
+
+ # Correct the initial state of the form to prevent the .NET maximized form issue.
+ $formInstallationPrompt.WindowState = [System.Windows.Forms.FormWindowState]::Normal
+ $formInstallationPrompt.BringToFront()
+
+ # Get the start position of the form so we can return the form to this position if PersistPrompt is enabled.
+ $formInstallationPromptStartLocation = $formInstallationPrompt.Location
+ }
+
+ # Built out timer
+ $installPromptTimer = [System.Windows.Forms.Timer]::new()
+ $installPromptTimer.Interval = $Timeout * 1000
+ $installPromptTimer.add_Tick($installPromptTimer_Tick)
+
+ # Built out timer for Persist Prompt mode.
+ $installPromptTimerPersist = [System.Windows.Forms.Timer]::new()
+ $installPromptTimerPersist.Interval = $adtConfig.UI.DefaultPromptPersistInterval * 1000
+ $installPromptTimerPersist.add_Tick($installPromptTimerPersist_Tick)
+
+ # Picture Banner.
+ $pictureBanner = [System.Windows.Forms.PictureBox]::new()
+ $pictureBanner.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
+ $pictureBanner.MinimumSize = $pictureBanner.ClientSize = $pictureBanner.MaximumSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, $Script:Dialogs.Classic.BannerHeight)
+ $pictureBanner.Location = [System.Drawing.Point]::new(0, 0)
+ $pictureBanner.Name = 'PictureBanner'
+ $pictureBanner.Image = $Script:Dialogs.Classic.Assets.Banner
+ $pictureBanner.Margin = $paddingNone
+ $pictureBanner.TabStop = $false
+
+ # Label Text.
+ $labelMessage = [System.Windows.Forms.Label]::new()
+ $labelMessage.MinimumSize = $labelMessage.ClientSize = $labelMessage.MaximumSize = [System.Drawing.Size]::new(381, 0)
+ $labelMessage.Margin = [System.Windows.Forms.Padding]::new(0, 10, 0, 5)
+ $labelMessage.Padding = [System.Windows.Forms.Padding]::new(20, 0, 20, 0)
+ $labelMessage.Anchor = [System.Windows.Forms.AnchorStyles]::None
+ $labelMessage.Font = $Script:Dialogs.Classic.Font
+ $labelMessage.Name = 'LabelMessage'
+ $labelMessage.Text = $Message
+ $labelMessage.TextAlign = [System.Drawing.ContentAlignment]::"Middle$MessageAlignment"
+ $labelMessage.TabStop = $false
+ $labelMessage.AutoSize = $true
+
+ # Picture Icon.
+ if ($Icon)
+ {
+ $pictureIcon = [System.Windows.Forms.PictureBox]::new()
+ $pictureIcon.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::CenterImage
+ $pictureIcon.MinimumSize = $pictureIcon.ClientSize = $pictureIcon.MaximumSize = [System.Drawing.Size]::new(64, 32)
+ $pictureIcon.Margin = [System.Windows.Forms.Padding]::new(0, 10, 0, 5)
+ $pictureIcon.Padding = [System.Windows.Forms.Padding]::new(24, 0, 8, 0)
+ $pictureIcon.Anchor = [System.Windows.Forms.AnchorStyles]::None
+ $pictureIcon.Name = 'PictureIcon'
+ $pictureIcon.Image = ([System.Drawing.SystemIcons]::$Icon).ToBitmap()
+ $pictureIcon.TabStop = $false
+ $pictureIcon.Height = $labelMessage.Height
+ }
+
+ # Button Abort (Hidden).
+ $buttonAbort = [System.Windows.Forms.Button]::new()
+ $buttonAbort.MinimumSize = $buttonAbort.ClientSize = $buttonAbort.MaximumSize = [System.Drawing.Size]::new(0, 0)
+ $buttonAbort.Margin = $buttonAbort.Padding = $paddingNone
+ $buttonAbort.DialogResult = [System.Windows.Forms.DialogResult]::Abort
+ $buttonAbort.Name = 'ButtonAbort'
+ $buttonAbort.Font = $Script:Dialogs.Classic.Font
+ $buttonAbort.BackColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.ForeColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.FlatAppearance.BorderSize = 0
+ $buttonAbort.FlatAppearance.MouseDownBackColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.FlatStyle = [System.Windows.Forms.FlatStyle]::System
+ $buttonAbort.TabStop = $false
+ $buttonAbort.Visible = $true # Has to be set visible so we can call Click on it.
+ $buttonAbort.UseVisualStyleBackColor = $true
+
+ # Button Default (Hidden).
+ $buttonDefault = [System.Windows.Forms.Button]::new()
+ $buttonDefault.MinimumSize = $buttonDefault.ClientSize = $buttonDefault.MaximumSize = [System.Drawing.Size]::new(0, 0)
+ $buttonDefault.Margin = $buttonDefault.Padding = $paddingNone
+ $buttonDefault.Name = 'buttonDefault'
+ $buttonDefault.Font = $Script:Dialogs.Classic.Font
+ $buttonDefault.BackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.ForeColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatAppearance.BorderSize = 0
+ $buttonDefault.FlatAppearance.MouseDownBackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatStyle = [System.Windows.Forms.FlatStyle]::System
+ $buttonDefault.TabStop = $false
+ $buttonDefault.Enabled = $false
+ $buttonDefault.Visible = $true # Has to be set visible so we can call Click on it.
+ $buttonDefault.UseVisualStyleBackColor = $true
+
+ # FlowLayoutPanel.
+ $flowLayoutPanel = [System.Windows.Forms.FlowLayoutPanel]::new()
+ $flowLayoutPanel.SuspendLayout()
+ $flowLayoutPanel.MinimumSize = $flowLayoutPanel.ClientSize = $flowLayoutPanel.MaximumSize = $controlSize
+ $flowLayoutPanel.Location = [System.Drawing.Point]::new(0, $Script:Dialogs.Classic.BannerHeight)
+ $flowLayoutPanel.AutoSize = $true
+ $flowLayoutPanel.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
+ $flowLayoutPanel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
+ $flowLayoutPanel.WrapContents = $true
+ $flowLayoutPanel.Margin = $flowLayoutPanel.Padding = $paddingNone
+
+ # Make sure label text is positioned correctly before adding it.
+ if ($Icon)
+ {
+ $labelMessage.Padding = [System.Windows.Forms.Padding]::new(0, 0, 10, 0)
+ $labelMessage.Location = [System.Drawing.Point]::new(64, 0)
+ $pictureIcon.Location = [System.Drawing.Point]::new(0, 0)
+ $flowLayoutPanel.Controls.Add($pictureIcon)
+ }
+ else
+ {
+ $labelMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelMessage.Location = [System.Drawing.Point]::new(0, 0)
+ $labelMessage.MinimumSize = $labelMessage.ClientSize = $labelMessage.MaximumSize = $controlSize
+ }
+ $flowLayoutPanel.Controls.Add($labelMessage)
+
+ # Add in remaining controls and resume object.
+ if ($ButtonLeftText -or $ButtonMiddleText -or $ButtonRightText)
+ {
+ # ButtonsPanel.
+ $panelButtons = [System.Windows.Forms.Panel]::new()
+ $panelButtons.SuspendLayout()
+ $panelButtons.MinimumSize = $panelButtons.ClientSize = $panelButtons.MaximumSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, 39)
+ $panelButtons.Margin = [System.Windows.Forms.Padding]::new(0, 10, 0, 0)
+ $panelButtons.AutoSize = $true
+ if ($Icon)
+ {
+ $panelButtons.Location = [System.Drawing.Point]::new(64, 0)
+ }
+ else
+ {
+ $panelButtons.Padding = $paddingNone
+ }
+
+ # Build out and add the buttons if we have any.
+ if ($ButtonLeftText)
+ {
+ # Button Left.
+ $buttonLeft = [System.Windows.Forms.Button]::new()
+ $buttonLeft.MinimumSize = $buttonLeft.ClientSize = $buttonLeft.MaximumSize = $buttonSize
+ $buttonLeft.Margin = $buttonLeft.Padding = $paddingNone
+ $buttonLeft.Location = [System.Drawing.Point]::new(14, 4)
+ $buttonLeft.DialogResult = [System.Windows.Forms.DialogResult]::No
+ $buttonLeft.Font = $Script:Dialogs.Classic.Font
+ $buttonLeft.Name = 'ButtonLeft'
+ $buttonLeft.Text = $ButtonLeftText
+ $buttonLeft.TabIndex = 0
+ $buttonLeft.AutoSize = $false
+ $buttonLeft.UseVisualStyleBackColor = $true
+ $panelButtons.Controls.Add($buttonLeft)
+ }
+ if ($ButtonMiddleText)
+ {
+ # Button Middle.
+ $buttonMiddle = [System.Windows.Forms.Button]::new()
+ $buttonMiddle.MinimumSize = $buttonMiddle.ClientSize = $buttonMiddle.MaximumSize = $buttonSize
+ $buttonMiddle.Margin = $buttonMiddle.Padding = $paddingNone
+ $buttonMiddle.Location = [System.Drawing.Point]::new(160, 4)
+ $buttonMiddle.DialogResult = [System.Windows.Forms.DialogResult]::Ignore
+ $buttonMiddle.Font = $Script:Dialogs.Classic.Font
+ $buttonMiddle.Name = 'ButtonMiddle'
+ $buttonMiddle.Text = $ButtonMiddleText
+ $buttonMiddle.TabIndex = 1
+ $buttonMiddle.AutoSize = $false
+ $buttonMiddle.UseVisualStyleBackColor = $true
+ $panelButtons.Controls.Add($buttonMiddle)
+ }
+ if ($ButtonRightText)
+ {
+ # Button Right.
+ $buttonRight = [System.Windows.Forms.Button]::new()
+ $buttonRight.MinimumSize = $buttonRight.ClientSize = $buttonRight.MaximumSize = $buttonSize
+ $buttonRight.Margin = $buttonRight.Padding = $paddingNone
+ $buttonRight.Location = [System.Drawing.Point]::new(306, 4)
+ $buttonRight.DialogResult = [System.Windows.Forms.DialogResult]::Yes
+ $buttonRight.Font = $Script:Dialogs.Classic.Font
+ $buttonRight.Name = 'ButtonRight'
+ $buttonRight.Text = $ButtonRightText
+ $buttonRight.TabIndex = 2
+ $buttonRight.AutoSize = $false
+ $buttonRight.UseVisualStyleBackColor = $true
+ $panelButtons.Controls.Add($buttonRight)
+ }
+
+ # Add the button panel in if we have buttons.
+ if ($panelButtons.Controls.Count)
+ {
+ $panelButtons.ResumeLayout()
+ $flowLayoutPanel.Controls.Add($panelButtons)
+ }
+ }
+ $flowLayoutPanel.ResumeLayout()
+
+ # Form Installation Prompt.
+ $formInstallationPromptStartLocation = $null
+ $formInstallationPrompt = [System.Windows.Forms.Form]::new()
+ $formInstallationPrompt.SuspendLayout()
+ $formInstallationPrompt.ClientSize = $controlSize
+ $formInstallationPrompt.Margin = $formInstallationPrompt.Padding = $paddingNone
+ $formInstallationPrompt.Font = $Script:Dialogs.Classic.Font
+ $formInstallationPrompt.Name = 'InstallPromptForm'
+ $formInstallationPrompt.Text = $Title
+ $formInstallationPrompt.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
+ $formInstallationPrompt.AutoScaleDimensions = [System.Drawing.SizeF]::new(7, 15)
+ $formInstallationPrompt.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
+ $formInstallationPrompt.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D
+ $formInstallationPrompt.MaximizeBox = $false
+ $formInstallationPrompt.MinimizeBox = $false
+ $formInstallationPrompt.TopMost = !$NotTopMost
+ $formInstallationPrompt.TopLevel = $true
+ $formInstallationPrompt.AutoSize = $true
+ $formInstallationPrompt.Icon = $Script:Dialogs.Classic.Assets.Icon
+ $formInstallationPrompt.Controls.Add($pictureBanner)
+ $formInstallationPrompt.Controls.Add($buttonAbort)
+ $formInstallationPrompt.Controls.Add($buttonDefault)
+ $formInstallationPrompt.Controls.Add($flowLayoutPanel)
+ $formInstallationPrompt.add_Load($formInstallationPrompt_Load)
+ $formInstallationPrompt.add_FormClosed($formInstallationPrompt_FormClosed)
+ $formInstallationPrompt.AcceptButton = $buttonDefault
+ $formInstallationPrompt.ActiveControl = $buttonDefault
+ $formInstallationPrompt.ResumeLayout()
+
+ # Start the timer.
+ $installPromptTimer.Start()
+ if ($PersistPrompt) { $installPromptTimerPersist.Start() }
+
+ # Show the prompt synchronously. If user cancels, then keep showing it until user responds using one of the buttons.
+ do
+ {
+ # Minimize all other windows
+ if ($MinimizeWindows)
+ {
+ $null = $adtEnv.ShellApp.MinimizeAll()
+ }
+
+ # Show the Form
+ $formResult = $formInstallationPrompt.ShowDialog()
+ }
+ until ($formResult -match '^(Yes|No|Ignore|Abort)$')
+
+ # Return the button text to the caller.
+ switch ($formResult)
+ {
+ Yes
+ {
+ return $ButtonRightText
+ }
+ No
+ {
+ return $ButtonLeftText
+ }
+ Ignore
+ {
+ return $ButtonMiddleText
+ }
+ Abort
+ {
+ # Restore minimized windows.
+ if ($MinimizeWindows)
+ {
+ $null = $adtEnv.ShellApp.UndoMinimizeAll()
+ }
+ if (!$NoExitOnTimeout)
+ {
+ if (& $Script:CommandTable.'Test-ADTSessionActive')
+ {
+ & $Script:CommandTable.'Close-ADTSession' -ExitCode $adtConfig.UI.DefaultExitCode
+ }
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'UI timed out but -NoExitOnTimeout specified. Continue...'
+ }
+ break
+ }
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationPromptFluent
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationPromptFluent
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Subtitle,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Message,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonRightText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonLeftText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonMiddleText,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$Timeout,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Send this straight out to the C# backend.
+ return [PSADT.UserInterface.UnifiedADTApplication]::ShowCustomDialog(
+ [System.TimeSpan]::FromSeconds($Timeout),
+ $Title,
+ $Subtitle,
+ !$NotTopMost,
+ (& $Script:CommandTable.'Get-ADTConfig').Assets.Logo,
+ $Message,
+ $ButtonLeftText,
+ $ButtonMiddleText,
+ $ButtonRightText
+ )
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationRestartPromptClassic
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationRestartPromptClassic
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'formRestartPromptStartLocation', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'CountdownNoHideSeconds', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$CountdownSeconds,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$CountdownNoHideSeconds,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Initialize variables.
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+
+ # Initalise the classic assets.
+ & $Script:CommandTable.'Initialize-ADTClassicAssets'
+
+ # Define starting counters.
+ $startTime = [System.DateTime]::Now
+ $countdownTime = $startTime
+
+ # Set up some default values.
+ $controlSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, 0)
+ $paddingNone = [System.Windows.Forms.Padding]::new(0, 0, 0, 0)
+ $buttonSize = [System.Drawing.Size]::new(195, 24)
+
+ # Define events for form windows.
+ $formRestart_Load = {
+ # Disable the X button.
+ try
+ {
+ & $Script:CommandTable.'Disable-ADTWindowCloseButton' -WindowHandle $formRestart.Handle
+ }
+ catch
+ {
+ # Not a terminating error if we can't disable the button. Just disable the Control Box instead
+ & $Script:CommandTable.'Write-ADTLogEntry' 'Failed to disable the Close button. Disabling the Control Box instead.' -Severity 2
+ $formRestart.ControlBox = $false
+ }
+
+ # Initialize the countdown timer.
+ $currentTime = [System.DateTime]::Now
+ $countdownTime = $startTime.AddSeconds($CountdownSeconds)
+ $timerCountdown.Start()
+
+ # Set up the form.
+ $remainingTime = $countdownTime.Subtract($currentTime)
+ $labelCountdown.Text = [System.String]::Format('{0}:{1:d2}:{2:d2}', $remainingTime.Days * 24 + $remainingTime.Hours, $remainingTime.Minutes, $remainingTime.Seconds)
+ if ($remainingTime.TotalSeconds -le $CountdownNoHideSeconds)
+ {
+ $buttonRestartLater.Enabled = $false
+ }
+
+ # Correct the initial state of the form to prevent the .NET maximized form issue.
+ $formRestart.WindowState = [System.Windows.Forms.FormWindowState]::Normal
+ $formRestart.BringToFront()
+
+ # Get the start position of the form so we can return the form to this position if PersistPrompt is enabled.
+ $formRestartPromptStartLocation = $formRestart.Location
+ }
+ $restartTimerPersist_Tick = {
+ # Show the Restart Popup.
+ $formRestart.WindowState = [System.Windows.Forms.FormWindowState]::Normal
+ $formRestart.TopMost = !$NotTopMost
+ $formRestart.Location = $formRestartPromptStartLocation
+ $formRestart.BringToFront()
+ }
+ $buttonRestartLater_Click = {
+ # Minimize the form.
+ $formRestart.WindowState = [System.Windows.Forms.FormWindowState]::Minimized
+ if ($NoCountdown)
+ {
+ # Reset the persistence timer.
+ $restartTimerPersist.Stop()
+ $restartTimerPersist.Start()
+ }
+ }
+ $buttonRestartNow_Click = {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Forcefully restarting the computer...'
+ & $Script:CommandTable.'Restart-Computer' -Force
+ }
+ $timerCountdown_Tick = {
+ # Get the time information.
+ $currentTime = & $Script:CommandTable.'Get-Date'
+ $countdownTime = $startTime.AddSeconds($CountdownSeconds)
+ $remainingTime = $countdownTime.Subtract($currentTime)
+
+ # If the countdown is complete, restart the machine.
+ if ($countdownTime -le $currentTime)
+ {
+ $buttonRestartNow.PerformClick()
+ }
+ else
+ {
+ # Update the form.
+ $labelCountdown.Text = [String]::Format('{0}:{1:d2}:{2:d2}', $remainingTime.Days * 24 + $remainingTime.Hours, $remainingTime.Minutes, $remainingTime.Seconds)
+ if ($remainingTime.TotalSeconds -le $CountdownNoHideSeconds)
+ {
+ $buttonRestartLater.Enabled = $false
+
+ # If the form is hidden when we hit the "No Hide", bring it back up.
+ If ($formRestart.WindowState.Equals([System.Windows.Forms.FormWindowState]::Minimized))
+ {
+ $formRestart.WindowState = [System.Windows.Forms.FormWindowState]::Normal
+ $formRestart.TopMost = !$NotTopMost
+ $formRestart.Location = $formRestartPromptStartLocation
+ $formRestart.BringToFront()
+ }
+ }
+ }
+ }
+ $formRestart_FormClosed = {
+ $timerCountdown.remove_Tick($timerCountdown_Tick)
+ $restartTimerPersist.remove_Tick($restartTimerPersist_Tick)
+ $buttonRestartNow.remove_Click($buttonRestartNow_Click)
+ $buttonRestartLater.remove_Click($buttonRestartLater_Click)
+ $formRestart.remove_Load($formRestart_Load)
+ $formRestart.remove_FormClosed($formRestart_FormClosed)
+ }
+ $formRestart_FormClosing = {
+ if ($_.CloseReason -eq 'UserClosing')
+ {
+ $_.Cancel = $true
+ }
+ }
+
+ # Persistence Timer.
+ $timerCountdown = [System.Windows.Forms.Timer]::new()
+ $restartTimerPersist = [System.Windows.Forms.Timer]::new()
+ $restartTimerPersist.Interval = $adtConfig.UI.RestartPromptPersistInterval * 1000
+ $restartTimerPersist.add_Tick($restartTimerPersist_Tick)
+ if ($NoCountdown)
+ {
+ $restartTimerPersist.Start()
+ }
+
+ # Picture Banner.
+ $pictureBanner = [System.Windows.Forms.PictureBox]::new()
+ $pictureBanner.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
+ $pictureBanner.MinimumSize = $pictureBanner.ClientSize = $pictureBanner.MaximumSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, $Script:Dialogs.Classic.BannerHeight)
+ $pictureBanner.Location = [System.Drawing.Point]::new(0, 0)
+ $pictureBanner.Name = 'PictureBanner'
+ $pictureBanner.Image = $Script:Dialogs.Classic.Assets.Banner
+ $pictureBanner.Margin = $paddingNone
+ $pictureBanner.TabStop = $false
+
+ # Label Message.
+ $labelMessage = [System.Windows.Forms.Label]::new()
+ $labelMessage.MinimumSize = $labelMessage.ClientSize = $labelMessage.MaximumSize = $controlSize
+ $labelMessage.Margin = [System.Windows.Forms.Padding]::new(0, 10, 0, 5)
+ $labelMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelMessage.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $labelMessage.Font = $Script:Dialogs.Classic.Font
+ $labelMessage.Name = 'LabelMessage'
+ $labelMessage.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelMessage.TabStop = $false
+ $labelMessage.AutoSize = $true
+ $labelMessage.Text = if ($NoCountdown)
+ {
+ $adtStrings.RestartPrompt.Message
+ }
+ else
+ {
+ "$($adtStrings.RestartPrompt.Message) $($adtStrings.RestartPrompt.MessageTime)`n`n$($adtStrings.RestartPrompt.MessageRestart)"
+ }
+
+ # Label Countdown.
+ $labelCountdown = [System.Windows.Forms.Label]::new()
+ $labelCountdown.MinimumSize = $labelCountdown.ClientSize = $labelCountdown.MaximumSize = $controlSize
+ $labelCountdown.Margin = $paddingNone
+ $labelCountdown.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelCountdown.Font = [System.Drawing.Font]::new($Script:Dialogs.Classic.Font.Name, ($Script:Dialogs.Classic.Font.Size + 9), [System.Drawing.FontStyle]::Bold)
+ $labelCountdown.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelCountdown.Text = '00:00:00'
+ $labelCountdown.Name = 'LabelCountdown'
+ $labelCountdown.TabStop = $false
+ $labelCountdown.AutoSize = $true
+
+ # Panel Flow Layout.
+ $flowLayoutPanel = [System.Windows.Forms.FlowLayoutPanel]::new()
+ $flowLayoutPanel.SuspendLayout()
+ $flowLayoutPanel.MinimumSize = $flowLayoutPanel.ClientSize = $flowLayoutPanel.MaximumSize = $controlSize
+ $flowLayoutPanel.Location = [System.Drawing.Point]::new(0, $Script:Dialogs.Classic.BannerHeight)
+ $flowLayoutPanel.Margin = $flowLayoutPanel.Padding = $paddingNone
+ $flowLayoutPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
+ $flowLayoutPanel.AutoSize = $true
+ $flowLayoutPanel.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
+ $flowLayoutPanel.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $flowLayoutPanel.WrapContents = $true
+ $flowLayoutPanel.Controls.Add($labelMessage)
+ if (!$NoCountdown)
+ {
+ # Label Time remaining message.
+ $labelTimeRemaining = [System.Windows.Forms.Label]::new()
+ $labelTimeRemaining.MinimumSize = $labelTimeRemaining.ClientSize = $labelTimeRemaining.MaximumSize = $controlSize
+ $labelTimeRemaining.Margin = $paddingNone
+ $labelTimeRemaining.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelTimeRemaining.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $labelTimeRemaining.Font = [System.Drawing.Font]::new($Script:Dialogs.Classic.Font.Name, ($Script:Dialogs.Classic.Font.Size + 3), [System.Drawing.FontStyle]::Bold)
+ $labelTimeRemaining.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelTimeRemaining.Text = $adtStrings.RestartPrompt.TimeRemaining
+ $labelTimeRemaining.Name = 'LabelTimeRemaining'
+ $labelTimeRemaining.TabStop = $false
+ $labelTimeRemaining.AutoSize = $true
+ $flowLayoutPanel.Controls.Add($labelTimeRemaining)
+ $flowLayoutPanel.Controls.Add($labelCountdown)
+ }
+
+ # Button Panel.
+ $panelButtons = [System.Windows.Forms.Panel]::new()
+ $panelButtons.SuspendLayout()
+ $panelButtons.MinimumSize = $panelButtons.ClientSize = $panelButtons.MaximumSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, 39)
+ $panelButtons.Margin = [System.Windows.Forms.Padding]::new(0, 10, 0, 0)
+ $panelButtons.Padding = $paddingNone
+ $panelButtons.AutoSize = $true
+
+ # Button Restart Now.
+ $buttonRestartNow = [System.Windows.Forms.Button]::new()
+ $buttonRestartNow.MinimumSize = $buttonRestartNow.ClientSize = $buttonRestartNow.MaximumSize = $buttonSize
+ $buttonRestartNow.Location = [System.Drawing.Point]::new(14, 4)
+ $buttonRestartNow.Margin = $buttonRestartNow.Padding = $paddingNone
+ $buttonRestartNow.Name = 'ButtonRestartNow'
+ $buttonRestartNow.Font = $Script:Dialogs.Classic.Font
+ $buttonRestartNow.Text = $adtStrings.RestartPrompt.ButtonRestartNow
+ $buttonRestartNow.TabIndex = 1
+ $buttonRestartNow.AutoSize = $true
+ $buttonRestartNow.UseVisualStyleBackColor = $true
+ $buttonRestartNow.add_Click($buttonRestartNow_Click)
+ $panelButtons.Controls.Add($buttonRestartNow)
+
+ # Button Minimize.
+ $buttonRestartLater = [System.Windows.Forms.Button]::new()
+ $buttonRestartLater.MinimumSize = $buttonRestartLater.ClientSize = $buttonRestartLater.MaximumSize = $buttonSize
+ $buttonRestartLater.Location = [System.Drawing.Point]::new(240, 4)
+ $buttonRestartLater.Margin = $buttonRestartLater.Padding = $paddingNone
+ $buttonRestartLater.Name = 'ButtonRestartLater'
+ $buttonRestartLater.Font = $Script:Dialogs.Classic.Font
+ $buttonRestartLater.Text = $adtStrings.RestartPrompt.ButtonRestartLater
+ $buttonRestartLater.TabIndex = 0
+ $buttonRestartLater.AutoSize = $true
+ $buttonRestartLater.UseVisualStyleBackColor = $true
+ $buttonRestartLater.add_Click($buttonRestartLater_Click)
+ $panelButtons.Controls.Add($buttonRestartLater)
+ $panelButtons.ResumeLayout()
+
+ # Add the Buttons Panel to the flowPanel.
+ $flowLayoutPanel.Controls.Add($panelButtons)
+ $flowLayoutPanel.ResumeLayout()
+
+ # Button Default (Hidden).
+ $buttonDefault = [System.Windows.Forms.Button]::new()
+ $buttonDefault.MinimumSize = $buttonDefault.ClientSize = $buttonDefault.MaximumSize = [System.Drawing.Size]::new(0, 0)
+ $buttonDefault.Margin = $buttonDefault.Padding = $paddingNone
+ $buttonDefault.Name = 'buttonDefault'
+ $buttonDefault.Font = $Script:Dialogs.Classic.Font
+ $buttonDefault.BackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.ForeColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatAppearance.BorderSize = 0
+ $buttonDefault.FlatAppearance.MouseDownBackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatStyle = [System.Windows.Forms.FlatStyle]::System
+ $buttonDefault.TabStop = $false
+ $buttonDefault.Enabled = $false
+ $buttonDefault.Visible = $true # Has to be set visible so we can call Click on it.
+ $buttonDefault.UseVisualStyleBackColor = $true
+
+ # Form Restart.
+ $formRestartPromptStartLocation = $null
+ $formRestart = [System.Windows.Forms.Form]::new()
+ $formRestart.SuspendLayout()
+ $formRestart.ClientSize = $controlSize
+ $formRestart.Margin = $formRestart.Padding = $paddingNone
+ $formRestart.Font = $Script:Dialogs.Classic.Font
+ $formRestart.Name = 'FormRestart'
+ $formRestart.Text = $Title
+ $formRestart.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
+ $formRestart.AutoScaleDimensions = [System.Drawing.SizeF]::new(7, 15)
+ $formRestart.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
+ $formRestart.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D
+ $formRestart.MaximizeBox = $false
+ $formRestart.MinimizeBox = $false
+ $formRestart.TopMost = !$NotTopMost
+ $formRestart.TopLevel = $true
+ $formRestart.AutoSize = $true
+ $formRestart.Icon = $Script:Dialogs.Classic.Assets.Icon
+ $formRestart.Controls.Add($pictureBanner)
+ $formRestart.Controls.Add($flowLayoutPanel)
+ $formRestart.Controls.Add($buttonDefault)
+ $formRestart.add_Load($formRestart_Load)
+ $formRestart.add_FormClosed($formRestart_FormClosed)
+ $formRestart.add_FormClosing($formRestart_FormClosing)
+ $formRestart.AcceptButton = $buttonDefault
+ $formRestart.ActiveControl = $buttonDefault
+ $formRestart.ResumeLayout()
+
+ # Timer Countdown.
+ if (!$NoCountdown)
+ {
+ $timerCountdown.add_Tick($timerCountdown_Tick)
+ }
+
+ # Show the Form.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Displaying restart prompt with $(if ($NoCountdown) { 'no' } else { "a [$CountdownSeconds] second" }) countdown."
+ return $formRestart.ShowDialog()
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationRestartPromptFluent
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationRestartPromptFluent
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Subtitle,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$CountdownSeconds,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Perform initial setup.
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+
+ # Send this straight out to the C# backend.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Displaying restart prompt with $(if ($NoCountdown) { 'no' } else { "a [$CountdownSeconds] second" }) countdown."
+ $result = [PSADT.UserInterface.UnifiedADTApplication]::ShowRestartDialog(
+ $Title,
+ $Subtitle,
+ !$NotTopMost,
+ $adtConfig.Assets.Logo,
+ $adtStrings.RestartPrompt.TimeRemaining,
+ $(if (!$NoCountdown) { [System.TimeSpan]::FromSeconds($CountdownSeconds) }),
+ $adtStrings.RestartPrompt.Message,
+ $adtStrings.RestartPrompt.MessageRestart,
+ $adtStrings.RestartPrompt.ButtonRestartLater,
+ $adtStrings.RestartPrompt.ButtonRestartNow
+ )
+
+ # Restart the computer if the button was pushed.
+ if ($result.Equals('Restart'))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Forcefully restarting the computer...'
+ & $Script:CommandTable.'Restart-Computer' -Force
+ }
+
+ # Return the button's result to the caller.
+ return $result
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTWelcomePromptClassic
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTWelcomePromptClassic
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ProcessObjects', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DeploymentType,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Types.ProcessObject[]]$ProcessObjects,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if ($_.TotalSeconds -gt (& $Script:CommandTable.'Get-ADTConfig').UI.DefaultTimeout)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName CloseProcessesCountdown -ProvidedValue $_ -ExceptionMessage 'The close applications countdown time cannot be longer than the timeout specified in the config file.'))
+ }
+ return ($_ -ge 0)
+ })]
+ [System.TimeSpan]$CloseProcessesCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$DeferTimes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DeferDeadline,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ForceCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ForceCloseProcessesCountdown,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PersistPrompt,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$AllowDefer,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoMinimizeWindows,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$CustomText,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Perform initial setup.
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+
+ # Initalise the classic assets.
+ & $Script:CommandTable.'Initialize-ADTClassicAssets'
+
+ # Initialize variables.
+ $showCountdown = $false
+ $showCloseProcesses = $false
+ $showDeference = $false
+ $persistWindow = $false
+
+ # Initial form layout: Close Applications
+ if ($welcomeState.RunningProcessDescriptions)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Prompting the user to close application(s) [$($welcomeState.RunningProcessDescriptions -join ',')]..."
+ $showCloseProcesses = $true
+ }
+
+ # Initial form layout: Allow Deferral
+ if ($AllowDefer -and (($DeferTimes -ge 0) -or $DeferDeadline))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'The user has the option to defer.'
+ $showDeference = $true
+
+ # Remove the Z from universal sortable date time format, otherwise it could be converted to a different time zone.
+ if ($DeferDeadline)
+ {
+ $DeferDeadline = (& $Script:CommandTable.'Get-Date' -Date ($DeferDeadline -replace 'Z')).ToString()
+ }
+ }
+
+ # If deferral is being shown and 'close apps countdown' or 'persist prompt' was specified, enable those features.
+ if (!$showDeference)
+ {
+ if ($CloseProcessesCountdown -gt [System.TimeSpan]::Zero)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Close applications countdown has [$($CloseProcessesCountdown - $(if ($welcomeState.CloseProcessesCountdown) { $welcomeState.CloseProcessesCountdown.Elapsed } else { [System.TimeSpan]::Zero }))] seconds remaining."
+ $showCountdown = $true
+ }
+ }
+ elseif ($PersistPrompt)
+ {
+ $persistWindow = $true
+ }
+
+ # If 'force close apps countdown' was specified, enable that feature.
+ if ($ForceCloseProcessesCountdown)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Close applications countdown has [$($CloseProcessesCountdown - $(if ($welcomeState.CloseProcessesCountdown) { $welcomeState.CloseProcessesCountdown.Elapsed } else { [System.TimeSpan]::Zero }))] seconds remaining."
+ $showCountdown = $true
+ }
+
+ # If 'force countdown' was specified, enable that feature.
+ if ($ForceCountdown)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Countdown has [$($CloseProcessesCountdown - $(if ($welcomeState.CloseProcessesCountdown) { $welcomeState.CloseProcessesCountdown.Elapsed } else { [System.TimeSpan]::Zero }))] seconds remaining."
+ $showCountdown = $true
+ }
+
+ # Set up some default values.
+ $controlSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, 0)
+ $paddingNone = [System.Windows.Forms.Padding]::new(0, 0, 0, 0)
+ $buttonSize = [System.Drawing.Size]::new(138, 24)
+
+ # Add the timer if it doesn't already exist - this avoids the timer being reset if the continue button is clicked.
+ if (!$welcomeState.WelcomeTimer)
+ {
+ $welcomeState.WelcomeTimer = [System.Windows.Forms.Timer]::new()
+ }
+
+ # Define all form events.
+ $formWelcome_FormClosed = {
+ $welcomeState.WelcomeTimer.remove_Tick($welcomeTimer_Tick)
+ $welcomeTimerPersist.remove_Tick($welcomeTimerPersist_Tick)
+ $timerRunningProcesses.remove_Tick($timerRunningProcesses_Tick)
+ $formWelcome.remove_Load($formWelcome_Load)
+ $formWelcome.remove_FormClosed($formWelcome_FormClosed)
+ }
+ $formWelcome_Load = {
+ # Disable the X button.
+ try
+ {
+ & $Script:CommandTable.'Disable-ADTWindowCloseButton' -WindowHandle $formWelcome.Handle
+ }
+ catch
+ {
+ # Not a terminating error if we can't disable the button. Just disable the Control Box instead
+ & $Script:CommandTable.'Write-ADTLogEntry' 'Failed to disable the Close button. Disabling the Control Box instead.' -Severity 2
+ $formWelcome.ControlBox = $false
+ }
+
+ # Initialize the countdown timer.
+ if ($showCountdown -and !$welcomeState.CloseProcessesCountdown)
+ {
+ $welcomeState.CloseProcessesCountdown = [System.Diagnostics.Stopwatch]::StartNew()
+ $remainingTime = $CloseProcessesCountdown - $welcomeState.CloseProcessesCountdown.Elapsed
+ $labelCountdown.Text = [System.String]::Format('{0}:{1:d2}:{2:d2}', $remainingTime.Days * 24 + $remainingTime.Hours, $remainingTime.Minutes, $remainingTime.Seconds)
+ }
+ $welcomeState.WelcomeTimer.Start()
+
+ # Correct the initial state of the form to prevent the .NET maximized form issue.
+ $formWelcome.WindowState = [System.Windows.Forms.FormWindowState]::Normal
+ $formWelcome.BringToFront()
+
+ # Get the start position of the form so we can return the form to this position if PersistPrompt is enabled.
+ $welcomeState.FormStartLocation = $formWelcome.Location
+ }
+ if ($showCountdown)
+ {
+ $welcomeTimer_Tick = {
+ # If the countdown is complete, close the application(s) or continue.
+ if ($welcomeState.CloseProcessesCountdown.Elapsed -gt $CloseProcessesCountdown)
+ {
+ if ($ForceCountdown -and !$welcomeState.RunningProcessDescriptions)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Countdown timer has elapsed and no processes running. Force continue.'
+ $buttonContinue.PerformClick()
+ }
+ elseif ($ForceCountdown -and $showDeference)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Countdown timer has elapsed and deferrals remaining. Force deferral.'
+ $buttonDefer.PerformClick()
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Close application(s) countdown timer has elapsed. Force closing application(s).'
+ if ($buttonCloseProcesses.CanFocus)
+ {
+ $buttonCloseProcesses.PerformClick()
+ }
+ else
+ {
+ $buttonContinue.PerformClick()
+ }
+ }
+ }
+ else
+ {
+ # Update the form.
+ $remainingTime = $CloseProcessesCountdown - $welcomeState.CloseProcessesCountdown.Elapsed
+ $labelCountdown.Text = [System.String]::Format('{0}:{1:d2}:{2:d2}', $remainingTime.Days * 24 + $remainingTime.Hours, $remainingTime.Minutes, $remainingTime.Seconds)
+ }
+ }
+ }
+ else
+ {
+ $welcomeState.WelcomeTimer.Interval = $adtConfig.UI.DefaultTimeout * 1000
+ $welcomeTimer_Tick = {
+ $buttonAbort.PerformClick()
+ }
+ }
+ $welcomeTimerPersist_Tick = {
+ $formWelcome.WindowState = [System.Windows.Forms.FormWindowState]::Normal
+ $formWelcome.TopMost = !$NotTopMost
+ $formWelcome.Location = $welcomeState.FormStartLocation
+ $formWelcome.BringToFront()
+ }
+ $timerRunningProcesses_Tick = {
+ # Grab current list of running processes.
+ $dynamicRunningProcesses = & $Script:CommandTable.'Get-ADTRunningProcesses' -ProcessObjects $ProcessObjects -InformationAction SilentlyContinue
+ $dynamicRunningProcessDescriptions = $dynamicRunningProcesses | & $Script:CommandTable.'Select-Object' -ExpandProperty ProcessDescription | & $Script:CommandTable.'Sort-Object' -Unique
+ $previousRunningProcessDescriptions = $welcomeState.RunningProcessDescriptions
+
+ # Check the previous list against what's currently running.
+ if (& $Script:CommandTable.'Compare-Object' -ReferenceObject @($welcomeState.RunningProcessDescriptions | & $Script:CommandTable.'Select-Object') -DifferenceObject @($dynamicRunningProcessDescriptions | & $Script:CommandTable.'Select-Object'))
+ {
+ # Update the runningProcessDescriptions variable for the next time this function runs.
+ $listboxCloseProcesses.Items.Clear()
+ if (($welcomeState.RunningProcessDescriptions = $dynamicRunningProcessDescriptions))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The running processes have changed. Updating the apps to close: [$($welcomeState.RunningProcessDescriptions -join ',')]..."
+ $listboxCloseProcesses.Items.AddRange($welcomeState.RunningProcessDescriptions)
+ }
+ }
+
+ # If CloseProcesses processes were running when the prompt was shown, and they are subsequently detected to be closed while the form is showing, then close the form. The deferral and CloseProcesses conditions will be re-evaluated.
+ if ($previousRunningProcessDescriptions)
+ {
+ if (!$dynamicRunningProcesses)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Previously detected running processes are no longer running.'
+ $formWelcome.Dispose()
+ }
+ }
+ elseif ($dynamicRunningProcesses)
+ {
+ # If CloseProcesses processes were not running when the prompt was shown, and they are subsequently detected to be running while the form is showing, then close the form for relaunch. The deferral and CloseProcesses conditions will be re-evaluated.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'New running processes detected. Updating the form to prompt to close the running applications.'
+ $formWelcome.Dispose()
+ }
+ }
+
+ # Welcome Timer.
+ $welcomeState.WelcomeTimer.add_Tick($welcomeTimer_Tick)
+
+ # Persistence Timer.
+ $welcomeTimerPersist = [System.Windows.Forms.Timer]::new()
+ $welcomeTimerPersist.Interval = $adtConfig.UI.DefaultPromptPersistInterval * 1000
+ $welcomeTimerPersist.add_Tick($welcomeTimerPersist_Tick)
+ if ($persistWindow)
+ {
+ $welcomeTimerPersist.Start()
+ }
+
+ # Process Re-Enumeration Timer.
+ $timerRunningProcesses = [System.Windows.Forms.Timer]::new()
+ $timerRunningProcesses.Interval = $adtConfig.UI.DynamicProcessEvaluationInterval * 1000
+ $timerRunningProcesses.add_Tick($timerRunningProcesses_Tick)
+ if ($adtConfig.UI.DynamicProcessEvaluation)
+ {
+ $timerRunningProcesses.Start()
+ }
+
+ # Picture Banner.
+ $pictureBanner = [System.Windows.Forms.PictureBox]::new()
+ $pictureBanner.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
+ $pictureBanner.MinimumSize = $pictureBanner.ClientSize = $pictureBanner.MaximumSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, $Script:Dialogs.Classic.BannerHeight)
+ $pictureBanner.Location = [System.Drawing.Point]::new(0, 0)
+ $pictureBanner.Name = 'PictureBanner'
+ $pictureBanner.Image = $Script:Dialogs.Classic.Assets.Banner
+ $pictureBanner.Margin = $paddingNone
+ $pictureBanner.TabStop = $false
+
+ # Label Welcome Message.
+ $labelWelcomeMessage = [System.Windows.Forms.Label]::new()
+ $labelWelcomeMessage.MinimumSize = $labelWelcomeMessage.ClientSize = $labelWelcomeMessage.MaximumSize = $controlSize
+ $labelWelcomeMessage.Margin = [System.Windows.Forms.Padding]::new(0, 10, 0, 0)
+ $labelWelcomeMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelWelcomeMessage.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $labelWelcomeMessage.Font = $Script:Dialogs.Classic.Font
+ $labelWelcomeMessage.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelWelcomeMessage.Text = $adtStrings.DeferPrompt.WelcomeMessage
+ $labelWelcomeMessage.Name = 'LabelWelcomeMessage'
+ $labelWelcomeMessage.TabStop = $false
+ $labelWelcomeMessage.AutoSize = $true
+
+ # Label App Name.
+ $labelAppName = [System.Windows.Forms.Label]::new()
+ $labelAppName.MinimumSize = $labelAppName.ClientSize = $labelAppName.MaximumSize = $controlSize
+ $labelAppName.Margin = [System.Windows.Forms.Padding]::new(0, 5, 0, 5)
+ $labelAppName.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelAppName.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $labelAppName.Font = [System.Drawing.Font]::new($Script:Dialogs.Classic.Font.Name, ($Script:Dialogs.Classic.Font.Size + 3), [System.Drawing.FontStyle]::Bold)
+ $labelAppName.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelAppName.Text = $Title.Replace('&', '&&')
+ $labelAppName.Name = 'LabelAppName'
+ $labelAppName.TabStop = $false
+ $labelAppName.AutoSize = $true
+
+ # Listbox Close Applications.
+ $listBoxCloseProcesses = [System.Windows.Forms.ListBox]::new()
+ $listBoxCloseProcesses.MinimumSize = $listBoxCloseProcesses.ClientSize = $listBoxCloseProcesses.MaximumSize = [System.Drawing.Size]::new(420, 100)
+ $listBoxCloseProcesses.Margin = [System.Windows.Forms.Padding]::new(15, 0, 15, 0)
+ $listBoxCloseProcesses.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $listboxCloseProcesses.Font = $Script:Dialogs.Classic.Font
+ $listBoxCloseProcesses.FormattingEnabled = $true
+ $listBoxCloseProcesses.HorizontalScrollbar = $true
+ $listBoxCloseProcesses.Name = 'ListBoxCloseProcesses'
+ $listBoxCloseProcesses.TabIndex = 3
+ if ($welcomeState.RunningProcessDescriptions)
+ {
+ $null = $listboxCloseProcesses.Items.AddRange($welcomeState.RunningProcessDescriptions)
+ }
+
+ # Label Countdown.
+ $labelCountdown = [System.Windows.Forms.Label]::new()
+ $labelCountdown.MinimumSize = $labelCountdown.ClientSize = $labelCountdown.MaximumSize = $controlSize
+ $labelCountdown.Margin = $paddingNone
+ $labelCountdown.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelCountdown.Font = [System.Drawing.Font]::new($Script:Dialogs.Classic.Font.Name, ($Script:Dialogs.Classic.Font.Size + 9), [System.Drawing.FontStyle]::Bold)
+ $labelCountdown.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelCountdown.Text = '00:00:00'
+ $labelCountdown.Name = 'LabelCountdown'
+ $labelCountdown.TabStop = $false
+ $labelCountdown.AutoSize = $true
+
+ # Panel Flow Layout.
+ $flowLayoutPanel = [System.Windows.Forms.FlowLayoutPanel]::new()
+ $flowLayoutPanel.SuspendLayout()
+ $flowLayoutPanel.MinimumSize = $flowLayoutPanel.ClientSize = $flowLayoutPanel.MaximumSize = $controlSize
+ $flowLayoutPanel.Location = [System.Drawing.Point]::new(0, $Script:Dialogs.Classic.BannerHeight)
+ $flowLayoutPanel.Margin = $flowLayoutPanel.Padding = $paddingNone
+ $flowLayoutPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
+ $flowLayoutPanel.AutoSize = $true
+ $flowLayoutPanel.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
+ $flowLayoutPanel.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $flowLayoutPanel.WrapContents = $true
+ $flowLayoutPanel.Controls.Add($labelWelcomeMessage)
+ $flowLayoutPanel.Controls.Add($labelAppName)
+ if ($CustomText -and $adtStrings.WelcomePrompt.Classic.CustomMessage)
+ {
+ # Label CustomMessage.
+ $labelCustomMessage = [System.Windows.Forms.Label]::new()
+ $labelCustomMessage.MinimumSize = $labelCustomMessage.ClientSize = $labelCustomMessage.MaximumSize = $controlSize
+ $labelCustomMessage.Margin = [System.Windows.Forms.Padding]::new(0, 0, 0, 5)
+ $labelCustomMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelCustomMessage.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $labelCustomMessage.Font = $Script:Dialogs.Classic.Font
+ $labelCustomMessage.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelCustomMessage.Text = $adtStrings.WelcomePrompt.Classic.CustomMessage
+ $labelCustomMessage.Name = 'LabelCustomMessage'
+ $labelCustomMessage.TabStop = $false
+ $labelCustomMessage.AutoSize = $true
+ $flowLayoutPanel.Controls.Add($labelCustomMessage)
+ }
+ if ($showCloseProcesses)
+ {
+ # Label CloseProcessesMessage.
+ $labelCloseProcessesMessage = [System.Windows.Forms.Label]::new()
+ $labelCloseProcessesMessage.MinimumSize = $labelCloseProcessesMessage.ClientSize = $labelCloseProcessesMessage.MaximumSize = $controlSize
+ $labelCloseProcessesMessage.Margin = [System.Windows.Forms.Padding]::new(0, 0, 0, 5)
+ $labelCloseProcessesMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelCloseProcessesMessage.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $labelCloseProcessesMessage.Font = $Script:Dialogs.Classic.Font
+ $labelCloseProcessesMessage.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelCloseProcessesMessage.Text = $adtStrings.ClosePrompt.Message
+ $labelCloseProcessesMessage.Name = 'LabelCloseProcessesMessage'
+ $labelCloseProcessesMessage.TabStop = $false
+ $labelCloseProcessesMessage.AutoSize = $true
+ $flowLayoutPanel.Controls.Add($labelCloseProcessesMessage)
+
+ # Listbox Close Applications.
+ $flowLayoutPanel.Controls.Add($listBoxCloseProcesses)
+ }
+ if ($showDeference)
+ {
+ # Label Defer Expiry Message.
+ $labelDeferExpiryMessage = [System.Windows.Forms.Label]::new()
+ $labelDeferExpiryMessage.MinimumSize = $labelDeferExpiryMessage.ClientSize = $labelDeferExpiryMessage.MaximumSize = $controlSize
+ $labelDeferExpiryMessage.Margin = [System.Windows.Forms.Padding]::new(0, 0, 0, 5)
+ $labelDeferExpiryMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelDeferExpiryMessage.Font = $Script:Dialogs.Classic.Font
+ $labelDeferExpiryMessage.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelDeferExpiryMessage.Text = $adtStrings.DeferPrompt.ExpiryMessage
+ $labelDeferExpiryMessage.Name = 'LabelDeferExpiryMessage'
+ $labelDeferExpiryMessage.TabStop = $false
+ $labelDeferExpiryMessage.AutoSize = $true
+ $flowLayoutPanel.Controls.Add($labelDeferExpiryMessage)
+
+ # Label Defer Deadline.
+ $labelDeferDeadline = [System.Windows.Forms.Label]::new()
+ $labelDeferDeadline.MinimumSize = $labelDeferDeadline.ClientSize = $labelDeferDeadline.MaximumSize = $controlSize
+ $labelDeferDeadline.Margin = [System.Windows.Forms.Padding]::new(0, 0, 0, 5)
+ $labelDeferDeadline.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelDeferDeadline.Font = [System.Drawing.Font]::new($Script:Dialogs.Classic.Font.Name, $Script:Dialogs.Classic.Font.Size, [System.Drawing.FontStyle]::Bold)
+ $labelDeferDeadline.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelDeferDeadline.Name = 'LabelDeferDeadline'
+ $labelDeferDeadline.TabStop = $false
+ $labelDeferDeadline.AutoSize = $true
+ if ($DeferTimes -ge 0)
+ {
+ $labelDeferDeadline.Text = "$($adtStrings.DeferPrompt.RemainingDeferrals) $($DeferTimes + 1)"
+ }
+ if ($deferDeadline)
+ {
+ $labelDeferDeadline.Text = "$($adtStrings.DeferPrompt.Deadline) $deferDeadline"
+ }
+ $flowLayoutPanel.Controls.Add($labelDeferDeadline)
+
+ # Label Defer Expiry Message.
+ $labelDeferWarningMessage = [System.Windows.Forms.Label]::new()
+ $labelDeferWarningMessage.MinimumSize = $labelDeferWarningMessage.ClientSize = $labelDeferWarningMessage.MaximumSize = $controlSize
+ $labelDeferWarningMessage.Margin = [System.Windows.Forms.Padding]::new(0, 0, 0, 5)
+ $labelDeferWarningMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelDeferWarningMessage.Font = $Script:Dialogs.Classic.Font
+ $labelDeferWarningMessage.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelDeferWarningMessage.Text = $adtStrings.DeferPrompt.WarningMessage
+ $labelDeferWarningMessage.Name = 'LabelDeferWarningMessage'
+ $labelDeferWarningMessage.TabStop = $false
+ $labelDeferWarningMessage.AutoSize = $true
+ $flowLayoutPanel.Controls.Add($labelDeferWarningMessage)
+ }
+ if ($showCountdown)
+ {
+ # Label CountdownMessage.
+ $labelCountdownMessage = [System.Windows.Forms.Label]::new()
+ $labelCountdownMessage.MinimumSize = $labelCountdownMessage.ClientSize = $labelCountdownMessage.MaximumSize = $controlSize
+ $labelCountdownMessage.Margin = $paddingNone
+ $labelCountdownMessage.Padding = [System.Windows.Forms.Padding]::new(10, 0, 10, 0)
+ $labelCountdownMessage.Anchor = [System.Windows.Forms.AnchorStyles]::Top
+ $labelCountdownMessage.Font = [System.Drawing.Font]::new($Script:Dialogs.Classic.Font.Name, ($Script:Dialogs.Classic.Font.Size + 3), [System.Drawing.FontStyle]::Bold)
+ $labelCountdownMessage.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
+ $labelCountdownMessage.Name = 'LabelCountdownMessage'
+ $labelCountdownMessage.TabStop = $false
+ $labelCountdownMessage.AutoSize = $true
+ $labelCountdownMessage.Text = if ($ForceCountdown -or !$welcomeState.RunningProcessDescriptions)
+ {
+ [System.String]::Format($adtStrings.WelcomePrompt.Classic.CountdownMessage, $adtStrings.DeploymentType.$DeploymentType)
+ }
+ else
+ {
+ $adtStrings.ClosePrompt.CountdownMessage
+ }
+ $flowLayoutPanel.Controls.Add($labelCountdownMessage)
+
+ ## Label Countdown.
+ $flowLayoutPanel.Controls.Add($labelCountdown)
+ }
+
+ # Panel Buttons.
+ $panelButtons = [System.Windows.Forms.Panel]::new()
+ $panelButtons.SuspendLayout()
+ $panelButtons.MinimumSize = $panelButtons.ClientSize = $panelButtons.MaximumSize = [System.Drawing.Size]::new($Script:Dialogs.Classic.Width, 39)
+ $panelButtons.Margin = [System.Windows.Forms.Padding]::new(0, 10, 0, 0)
+ $panelButtons.Padding = $paddingNone
+ $panelButtons.AutoSize = $true
+ if ($showCloseProcesses)
+ {
+ # Button Close For Me.
+ $buttonCloseProcesses = [System.Windows.Forms.Button]::new()
+ $buttonCloseProcesses.MinimumSize = $buttonCloseProcesses.ClientSize = $buttonCloseProcesses.MaximumSize = $buttonSize
+ $buttonCloseProcesses.Margin = $buttonCloseProcesses.Padding = $paddingNone
+ $buttonCloseProcesses.Location = [System.Drawing.Point]::new(14, 4)
+ $buttonCloseProcesses.DialogResult = [System.Windows.Forms.DialogResult]::Yes
+ $buttonCloseProcesses.Font = $Script:Dialogs.Classic.Font
+ $buttonCloseProcesses.Name = 'ButtonCloseProcesses'
+ $buttonCloseProcesses.Text = $adtStrings.ClosePrompt.ButtonClose
+ $buttonCloseProcesses.TabIndex = 1
+ $buttonCloseProcesses.AutoSize = $true
+ $buttonCloseProcesses.UseVisualStyleBackColor = $true
+ $panelButtons.Controls.Add($buttonCloseProcesses)
+ }
+ if ($showDeference)
+ {
+ # Button Defer.
+ $buttonDefer = [System.Windows.Forms.Button]::new()
+ $buttonDefer.MinimumSize = $buttonDefer.ClientSize = $buttonDefer.MaximumSize = $buttonSize
+ $buttonDefer.Margin = $buttonDefer.Padding = $paddingNone
+ $buttonDefer.Location = [System.Drawing.Point]::new((14, 154)[$showCloseProcesses], 4)
+ $buttonDefer.DialogResult = [System.Windows.Forms.DialogResult]::No
+ $buttonDefer.Font = $Script:Dialogs.Classic.Font
+ $buttonDefer.Name = 'ButtonDefer'
+ $buttonDefer.Text = $adtStrings.ClosePrompt.ButtonDefer
+ $buttonDefer.TabIndex = 0
+ $buttonDefer.AutoSize = $true
+ $buttonDefer.UseVisualStyleBackColor = $true
+ $panelButtons.Controls.Add($buttonDefer)
+ }
+
+ # Button Continue.
+ $buttonContinue = [System.Windows.Forms.Button]::new()
+ $buttonContinue.MinimumSize = $buttonContinue.ClientSize = $buttonContinue.MaximumSize = $buttonSize
+ $buttonContinue.Margin = $buttonContinue.Padding = $paddingNone
+ $buttonContinue.Location = [System.Drawing.Point]::new(294, 4)
+ $buttonContinue.DialogResult = [System.Windows.Forms.DialogResult]::OK
+ $buttonContinue.Font = $Script:Dialogs.Classic.Font
+ $buttonContinue.Name = 'ButtonContinue'
+ $buttonContinue.Text = $adtStrings.ClosePrompt.ButtonContinue
+ $buttonContinue.TabIndex = 2
+ $buttonContinue.AutoSize = $true
+ $buttonContinue.UseVisualStyleBackColor = $true
+ if ($showCloseProcesses)
+ {
+ # Add tooltip to Continue button.
+ $toolTip = [System.Windows.Forms.ToolTip]::new()
+ $toolTip.BackColor = [Drawing.Color]::LightGoldenrodYellow
+ $toolTip.IsBalloon = $false
+ $toolTip.InitialDelay = 100
+ $toolTip.ReshowDelay = 100
+ $toolTip.SetToolTip($buttonContinue, $adtStrings.ClosePrompt.ButtonContinueTooltip)
+ }
+ $panelButtons.Controls.Add($buttonContinue)
+ $panelButtons.ResumeLayout()
+
+ # Add the Buttons Panel to the flowPanel.
+ $flowLayoutPanel.Controls.Add($panelButtons)
+ $flowLayoutPanel.ResumeLayout()
+
+ # Button Abort (Hidden).
+ $buttonAbort = [System.Windows.Forms.Button]::new()
+ $buttonAbort.MinimumSize = $buttonAbort.ClientSize = $buttonAbort.MaximumSize = [System.Drawing.Size]::new(0, 0)
+ $buttonAbort.Margin = $buttonAbort.Padding = $paddingNone
+ $buttonAbort.DialogResult = [System.Windows.Forms.DialogResult]::Abort
+ $buttonAbort.Name = 'buttonAbort'
+ $buttonAbort.Font = $Script:Dialogs.Classic.Font
+ $buttonAbort.BackColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.ForeColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.FlatAppearance.BorderSize = 0
+ $buttonAbort.FlatAppearance.MouseDownBackColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::Transparent
+ $buttonAbort.FlatStyle = [System.Windows.Forms.FlatStyle]::System
+ $buttonAbort.TabStop = $false
+ $buttonAbort.Visible = $true # Has to be set visible so we can call Click on it.
+ $buttonAbort.UseVisualStyleBackColor = $true
+
+ # Button Default (Hidden).
+ $buttonDefault = [System.Windows.Forms.Button]::new()
+ $buttonDefault.MinimumSize = $buttonDefault.ClientSize = $buttonDefault.MaximumSize = [System.Drawing.Size]::new(0, 0)
+ $buttonDefault.Margin = $buttonDefault.Padding = $paddingNone
+ $buttonDefault.Name = 'buttonDefault'
+ $buttonDefault.Font = $Script:Dialogs.Classic.Font
+ $buttonDefault.BackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.ForeColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatAppearance.BorderSize = 0
+ $buttonDefault.FlatAppearance.MouseDownBackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::Transparent
+ $buttonDefault.FlatStyle = [System.Windows.Forms.FlatStyle]::System
+ $buttonDefault.TabStop = $false
+ $buttonDefault.Enabled = $false
+ $buttonDefault.Visible = $true # Has to be set visible so we can call Click on it.
+ $buttonDefault.UseVisualStyleBackColor = $true
+
+ ## Form Welcome
+ $formWelcome = [System.Windows.Forms.Form]::new()
+ $formWelcome.SuspendLayout()
+ $formWelcome.ClientSize = $controlSize
+ $formWelcome.Margin = $formWelcome.Padding = $paddingNone
+ $formWelcome.Font = $Script:Dialogs.Classic.Font
+ $formWelcome.Name = 'WelcomeForm'
+ $formWelcome.Text = $Title
+ $formWelcome.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
+ $formWelcome.AutoScaleDimensions = [System.Drawing.SizeF]::new(7, 15)
+ $formWelcome.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
+ $formWelcome.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D
+ $formWelcome.MaximizeBox = $false
+ $formWelcome.MinimizeBox = $false
+ $formWelcome.TopMost = !$NotTopMost
+ $formWelcome.TopLevel = $true
+ $formWelcome.AutoSize = $true
+ $formWelcome.Icon = $Script:Dialogs.Classic.Assets.Icon
+ $formWelcome.Controls.Add($pictureBanner)
+ $formWelcome.Controls.Add($buttonAbort)
+ $formWelcome.Controls.Add($buttonDefault)
+ $formWelcome.Controls.Add($flowLayoutPanel)
+ $formWelcome.add_Load($formWelcome_Load)
+ $formWelcome.add_FormClosed($formWelcome_FormClosed)
+ $formWelcome.AcceptButton = $buttonDefault
+ $formWelcome.ActiveControl = $buttonDefault
+ $formWelcome.ResumeLayout()
+
+ # Minimize all other windows.
+ if (!$NoMinimizeWindows)
+ {
+ $null = (& $Script:CommandTable.'Get-ADTEnvironmentTable').ShellApp.MinimizeAll()
+ }
+
+ # Run the form and store the result.
+ $result = switch ($formWelcome.ShowDialog())
+ {
+ OK { 'Continue'; break }
+ No { 'Defer'; break }
+ Yes { 'Close'; break }
+ Abort { 'Timeout'; break }
+ }
+ $formWelcome.Dispose()
+
+ # Shut down the timer if its running.
+ if ($adtConfig.UI.DynamicProcessEvaluation)
+ {
+ $timerRunningProcesses.Stop()
+ }
+
+ # Return the result to the caller.
+ return $result
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTWelcomePromptFluent
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTWelcomePromptFluent
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'UnboundArguments', Justification = "This parameter is just to trap any superfluous input at the end of the function's call.")]
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Title,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Subtitle,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$DeferTimes,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoMinimizeWindows,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ # Perform initial setup.
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+
+ # Convert the incoming ProcessObject objects into AppProcessInfo objects.
+ $appsToClose = if ($welcomeState.RunningProcesses)
+ {
+ $welcomeState.RunningProcesses | & {
+ process
+ {
+ $_.Refresh(); if (!$_.HasExited)
+ {
+ # Get icon so we can convert it into a media image for the UI.
+ $icon = try
+ {
+ [PSADT.UserInterface.Utilities.ProcessExtensions]::GetIcon($_, $true)
+ }
+ catch
+ {
+ $null = $null
+ }
+
+ # Instantiate and return a new AppProcessInfo object.
+ return [PSADT.UserInterface.Services.AppProcessInfo]::new(
+ $_.ProcessName,
+ $_.ProcessDescription,
+ $_.Product,
+ $_.Company,
+ $(if ($icon) { [PSADT.UserInterface.Utilities.BitmapExtensions]::ConvertToImageSource($icon.ToBitmap()) }),
+ $_.StartTime
+ )
+ }
+ }
+ }
+ }
+
+ # Minimize all other windows.
+ if (!$NoMinimizeWindows)
+ {
+ $null = (& $Script:CommandTable.'Get-ADTEnvironmentTable').ShellApp.MinimizeAll()
+ }
+
+ # Send this out to the C# code.
+ $result = [PSADT.UserInterface.UnifiedADTApplication]::ShowWelcomeDialog(
+ [System.TimeSpan]::FromSeconds($adtConfig.UI.DefaultTimeout),
+ $Title,
+ $Subtitle,
+ !$NotTopMost,
+ $(if ($PSBoundParameters.ContainsKey('DeferTimes')) { $DeferTimes + 1 }),
+ $appsToClose,
+ $adtConfig.Assets.Logo,
+ $adtStrings.WelcomePrompt.Fluent.DialogMessage,
+ $adtStrings.WelcomePrompt.Fluent.DialogMessageNoProcesses,
+ $adtStrings.WelcomePrompt.Fluent.ButtonDeferRemaining,
+ $adtStrings.WelcomePrompt.Fluent.ButtonLeftText,
+ $adtStrings.WelcomePrompt.Fluent.ButtonRightText,
+ $adtStrings.WelcomePrompt.Fluent.ButtonRightTextNoProcesses,
+ $(if ($adtConfig.UI.DynamicProcessEvaluation) { [PSADT.UserInterface.Services.ProcessEvaluationService]::new() })
+ )
+
+ # Return a translated value that's compatible with the toolkit.
+ switch ($result)
+ {
+ Continue
+ {
+ return 'Close'
+ break
+ }
+ Defer
+ {
+ return 'Defer'
+ break
+ }
+ Cancel
+ {
+ return 'Timeout'
+ break
+ }
+ default
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("The returned dialog result of [$_] is invalid and cannot be processed.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = "WelcomeDialogInvalidResult"
+ TargetObject = $_
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTInstallationProgressRunning
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTInstallationProgressRunning
+{
+ # Return the value of the global state's bool.
+ return $Script:Dialogs.((& $Script:CommandTable.'Get-ADTConfig').UI.DialogStyle).ProgressWindow.Running
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTIsMultiSessionOS
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTIsMultiSessionOS
+{
+ # The registry is significantly cheaper to query than a CIM instance.
+ # https://www.jasonsamuel.com/2020/03/02/how-to-use-microsoft-wvd-windows-10-multi-session-fslogix-msix-app-attach-to-build-an-azure-powered-virtual-desktop-experience/
+ return ([Microsoft.Win32.Registry]::GetValue('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion', 'ProductName', $null) -match '^Microsoft Windows \d+ Enterprise (for Virtual Desktops|Multi-Session)$')
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTModuleIsReleaseBuild
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTModuleIsReleaseBuild
+{
+ return $Script:Module.Compiled -and $Script:Module.Signed
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTNonNativeCaller
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTNonNativeCaller
+{
+ return (& $Script:CommandTable.'Get-PSCallStack').Command.Contains('AppDeployToolkitMain.ps1')
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTReleaseBuildFileValidity
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTReleaseBuildFileValidity
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName LiteralPath -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (![System.IO.Directory]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName LiteralPath -ProvidedValue $_ -ExceptionMessage 'The specified directory does not exist.'))
+ }
+ return $_
+ })]
+ [System.String]$LiteralPath
+ )
+
+ # If we're running a release module, ensure the ps*1 files haven't been tampered with.
+ if ((& $Script:CommandTable.'Test-ADTModuleIsReleaseBuild') -and ($badFiles = & $Script:CommandTable.'Get-ChildItem' @PSBoundParameters -Filter *.ps*1 -Recurse | & $Script:CommandTable.'Get-AuthenticodeSignature' | & { process { if (!$_.Status.Equals([System.Management.Automation.SignatureStatus]::Valid)) { return $_ } } }))
+ {
+ return $badFiles
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Unblock-ADTAppExecutionInternal
+#
+#-----------------------------------------------------------------------------
+
+function Unblock-ADTAppExecutionInternal
+{
+ <#
+
+ .SYNOPSIS
+ Core logic used within Unblock-ADTAppExecution.
+
+ .DESCRIPTION
+ This function contains core logic used within Unblock-ADTAppExecution, separated out to facilitate calling via PowerShell without dependency on the toolkit.
+
+ .NOTES
+ This function deliberately does not use the module's CommandTable to ensure it can run without module dependency.
+
+ .LINK
+ https://psappdeploytoolkit.com
+
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'None')]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Tasks')]
+ [ValidateNotNullOrEmpty()]
+ [Microsoft.Management.Infrastructure.CimInstance[]]$Tasks,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'TaskName')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TaskName
+ )
+
+ # Remove Debugger values to unblock processes.
+ Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\*" -Name Debugger -Verbose:$false -ErrorAction Ignore | & {
+ process
+ {
+ if ($_.Debugger.Contains('PSAppDeployToolkit'))
+ {
+ Write-Verbose -Message "Removing the Image File Execution Options registry key to unblock execution of [$($_.PSChildName)]."
+ Remove-ItemProperty -LiteralPath $_.PSPath -Name Debugger -Verbose:$false
+ }
+ }
+ }
+
+ # Remove the scheduled task if it exists.
+ switch ($PSCmdlet.ParameterSetName)
+ {
+ TaskName
+ {
+ Write-Verbose -Message "Deleting Scheduled Task [$TaskName]."
+ Get-ScheduledTask -TaskName $TaskName -Verbose:$false -ErrorAction Ignore | Unregister-ScheduledTask -Confirm:$false -Verbose:$false
+ break
+ }
+ Tasks
+ {
+ Write-Verbose -Message "Deleting Scheduled Tasks ['$($Tasks.TaskName -join "', '")']."
+ $Tasks | Unregister-ScheduledTask -Confirm:$false -Verbose:$false
+ break
+ }
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Write-ADTLogEntryToInformationStream
+#
+#-----------------------------------------------------------------------------
+
+function Write-ADTLogEntryToInformationStream
+{
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Message,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Source,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Format,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.ConsoleColor]$ForegroundColor,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.ConsoleColor]$BackgroundColor
+ )
+
+ begin
+ {
+ # Remove parameters that aren't used to generate an InformationRecord object.
+ $null = $PSBoundParameters.Remove('Source')
+ $null = $PSBoundParameters.Remove('Format')
+
+ # Establish the base InformationRecord to write out.
+ $infoRecord = [System.Management.Automation.InformationRecord]::new([System.Management.Automation.HostInformationMessage]$PSBoundParameters, $Source)
+ }
+
+ process
+ {
+ # Update the message for piped operations and write out to the InformationStream.
+ $infoRecord.MessageData.Message = [System.String]::Format($Format, $Message)
+ $PSCmdlet.WriteInformation($infoRecord)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Add-ADTEdgeExtension
+#
+#-----------------------------------------------------------------------------
+
+function Add-ADTEdgeExtension
+{
+ <#
+ .SYNOPSIS
+ Adds an extension for Microsoft Edge using the ExtensionSettings policy.
+
+ .DESCRIPTION
+ This function adds an extension for Microsoft Edge using the ExtensionSettings policy: https://learn.microsoft.com/en-us/deployedge/microsoft-edge-manage-extensions-ref-guide.
+
+ This enables Edge Extensions to be installed and managed like applications, enabling extensions to be pushed to specific devices or users alongside existing GPO/Intune extension policies.
+
+ This should not be used in conjunction with Edge Management Service which leverages the same registry key to configure Edge extensions.
+
+ .PARAMETER ExtensionID
+ The ID of the extension to add.
+
+ .PARAMETER UpdateUrl
+ The update URL of the extension. This is the URL where the extension will check for updates.
+
+ .PARAMETER InstallationMode
+ The installation mode of the extension. Allowed values: blocked, allowed, removed, force_installed, normal_installed.
+
+ .PARAMETER MinimumVersionRequired
+ The minimum version of the extension required for installation.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Add-ADTEdgeExtension -ExtensionID "extensionID" -InstallationMode "force_installed" -UpdateUrl "https://edge.microsoft.com/extensionwebstorebase/v1/crx"
+
+ This example adds the specified extension to be force installed in Microsoft Edge.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Add-ADTEdgeExtension
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ExtensionID,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.Uri]::IsWellFormedUriString($_, [System.UriKind]::Absolute))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName UpdateUrl -ProvidedValue $_ -ExceptionMessage 'The specified input is not a valid URL.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$UpdateUrl,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('blocked', 'allowed', 'removed', 'force_installed', 'normal_installed')]
+ [System.String]$InstallationMode,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$MinimumVersionRequired
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Adding extension with ID [$ExtensionID] using installation mode [$InstallationMode] and update URL [$UpdateUrl]$(if ($MinimumVersionRequired) {" with minimum version required [$MinimumVersionRequired]"})."
+ try
+ {
+ try
+ {
+ # Set up the additional extension.
+ $additionalExtension = @{
+ installation_mode = $InstallationMode
+ update_url = $UpdateUrl
+ }
+
+ # Add in the minimum version if specified.
+ if ($MinimumVersionRequired)
+ {
+ $additionalExtension.Add('minimum_version_required', $MinimumVersionRequired)
+ }
+
+ # Get the current extensions from the registry, add our additional one, then convert the result back to JSON.
+ $extensionsSettings = & $Script:CommandTable.'Get-ADTEdgeExtensions' |
+ & $Script:CommandTable.'Add-Member' -Name $ExtensionID -Value $additionalExtension -MemberType NoteProperty -Force -PassThru |
+ & $Script:CommandTable.'ConvertTo-Json' -Compress
+
+ # Add the additional extension to the current values, then re-write the definition in the registry.
+ $null = & $Script:CommandTable.'Set-ADTRegistryKey' -Key Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge -Name ExtensionSettings -Value $extensionsSettings
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Add-ADTSessionClosingCallback
+#
+#-----------------------------------------------------------------------------
+
+function Add-ADTSessionClosingCallback
+{
+ <#
+ .SYNOPSIS
+ Adds a callback to be executed when the ADT session is closing.
+
+ .DESCRIPTION
+ The Add-ADTSessionClosingCallback function registers a callback command to be executed when the ADT session is closing. This function sends the callback to the backend function for processing.
+
+ .PARAMETER Callback
+ The callback command(s) to be executed when the ADT session is closing.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Add-ADTSessionClosingCallback -Callback $myCallback
+
+ This example adds the specified callback to be executed when the ADT session is closing.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Add-ADTSessionClosingCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Closing -Action Add @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Add-ADTSessionFinishingCallback
+#
+#-----------------------------------------------------------------------------
+
+function Add-ADTSessionFinishingCallback
+{
+ <#
+ .SYNOPSIS
+ Adds a callback to be executed when the ADT session is finishing.
+
+ .DESCRIPTION
+ The Add-ADTSessionFinishingCallback function registers a callback command to be executed when the ADT session is finishing. This function sends the callback to the backend function for processing.
+
+ .PARAMETER Callback
+ The callback command(s) to be executed when the ADT session is finishing.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Add-ADTSessionFinishingCallback -Callback $myCallback
+
+ This example adds the specified callback to be executed when the ADT session is finishing.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Add-ADTSessionFinishingCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Finishing -Action Add @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Add-ADTSessionOpeningCallback
+#
+#-----------------------------------------------------------------------------
+
+function Add-ADTSessionOpeningCallback
+{
+ <#
+ .SYNOPSIS
+ Adds a callback to be executed when the ADT session is opening.
+
+ .DESCRIPTION
+ The Add-ADTSessionOpeningCallback function registers a callback command to be executed when the ADT session is opening. This function sends the callback to the backend function for processing.
+
+ .PARAMETER Callback
+ The callback command(s) to be executed when the ADT session is opening.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Add-ADTSessionOpeningCallback -Callback $myCallback
+
+ This example adds the specified callback to be executed when the ADT session is opening.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Add-ADTSessionOpeningCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Opening -Action Add @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Add-ADTSessionStartingCallback
+#
+#-----------------------------------------------------------------------------
+
+function Add-ADTSessionStartingCallback
+{
+ <#
+ .SYNOPSIS
+ Adds a callback to be executed when the ADT session is starting.
+
+ .DESCRIPTION
+ The Add-ADTSessionStartingCallback function registers a callback command to be executed when the ADT session is starting. This function sends the callback to the backend function for processing.
+
+ .PARAMETER Callback
+ The callback command(s) to be executed when the ADT session is starting.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Add-ADTSessionStartingCallback -Callback $myCallback
+
+ This example adds the specified callback to be executed when the ADT session is starting.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Add-ADTSessionStartingCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Starting -Action Add @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Block-ADTAppExecution
+#
+#-----------------------------------------------------------------------------
+
+function Block-ADTAppExecution
+{
+ <#
+ .SYNOPSIS
+ Block the execution of an application(s).
+
+ .DESCRIPTION
+ This function is called when you pass the -BlockExecution parameter to the Stop-RunningApplications function. It does the following:
+
+ 1) Makes a copy of this script in a temporary directory on the local machine.
+ 2) Checks for an existing scheduled task from previous failed installation attempt where apps were blocked and if found, calls the Unblock-ADTAppExecution function to restore the original IFEO registry keys. This is to prevent the function from overriding the backup of the original IFEO options.
+ 3) Creates a scheduled task to restore the IFEO registry key values in case the script is terminated uncleanly by calling `Unblock-ADTAppExecution` the local temporary copy of this module.
+ 4) Modifies the "Image File Execution Options" registry key for the specified process(s) to call `Show-ADTInstallationPrompt` with the appropriate messaging via this module.
+ 5) When the script is called with those parameters, it will display a custom message to the user to indicate that execution of the application has been blocked while the installation is in progress. The text of this message can be customized in the strings.psd1 file.
+
+ .PARAMETER ProcessName
+ Name of the process or processes separated by commas.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Block-ADTAppExecution -ProcessName ('winword','excel')
+
+ This example blocks the execution of Microsoft Word and Excel.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ It is used when the -BlockExecution parameter is specified with the Show-ADTInstallationWelcome function to block applications.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Block-ADTAppExecution
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, HelpMessage = 'Specify process names, separated by commas.')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ProcessName
+ )
+
+ begin
+ {
+ # Get everything we need before commencing.
+ try
+ {
+ $adtSession = & $Script:CommandTable.'Get-ADTSession'
+ $adtEnv = & $Script:CommandTable.'Get-ADTEnvironmentTable'
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $taskName = "$($adtEnv.appDeployToolkitName)_$($adtSession.installName)_BlockedApps" -replace $adtEnv.InvalidScheduledTaskNameCharsRegExPattern
+ }
+
+ process
+ {
+ # Bypass if no Admin rights.
+ if (!$adtEnv.IsAdmin)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing Function [$($MyInvocation.MyCommand.Name)], because [User: $($adtEnv.ProcessNTAccount)] is not admin."
+ return
+ }
+
+ try
+ {
+ try
+ {
+ # Clean up any previous state that might be lingering.
+ if ($task = & $Script:CommandTable.'Get-ScheduledTask' -TaskName $taskName -ErrorAction Ignore)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Scheduled task [$taskName] already exists, running [Unblock-ADTAppExecution] to clean up previous state."
+ & $Script:CommandTable.'Unblock-ADTAppExecution' -Tasks $task
+ }
+
+ # Create a scheduled task to run on startup to call this script and clean up blocked applications in case the installation is interrupted, e.g. user shuts down during installation"
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Creating scheduled task to cleanup blocked applications in case the installation is interrupted.'
+ try
+ {
+ $nstParams = @{
+ Principal = & $Script:CommandTable.'New-ScheduledTaskPrincipal' -Id Author -UserId S-1-5-18
+ Trigger = & $Script:CommandTable.'New-ScheduledTaskTrigger' -AtStartup
+ Action = & $Script:CommandTable.'New-ScheduledTaskAction' -Execute $adtEnv.envPSProcessPath -Argument "-NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -EncodedCommand $(& $Script:CommandTable.'Out-ADTPowerShellEncodedCommand' -Command "& {$($Script:CommandTable.'Unblock-ADTAppExecutionInternal'.ScriptBlock)} -TaskName '$($taskName.Replace("'", "''"))'")"
+ Settings = & $Script:CommandTable.'New-ScheduledTaskSettingsSet' -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd -ExecutionTimeLimit ([System.TimeSpan]::FromHours(1))
+ }
+ $null = & $Script:CommandTable.'New-ScheduledTask' @nstParams | & $Script:CommandTable.'Register-ScheduledTask' -TaskName $taskName
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to create the scheduled task [$taskName]." -Severity 3
+ return
+ }
+
+ # Store the BlockExection command in the registry due to IFEO length issues when > 255 chars.
+ $blockExecRegPath = & $Script:CommandTable.'Convert-ADTRegistryPath' -Key (& $Script:CommandTable.'Join-Path' -Path $adtConfig.Toolkit.RegPath -ChildPath $adtEnv.appDeployToolkitName)
+ $blockExecCommand = "& (Import-Module -FullyQualifiedName @{ ModuleName = '$("$($Script:PSScriptRoot)\$($MyInvocation.MyCommand.Module.Name).psd1".Replace("'", "''"))'; Guid = '$($MyInvocation.MyCommand.Module.Guid)'; ModuleVersion = '$($MyInvocation.MyCommand.Module.Version)' } -PassThru) { & `$CommandTable.'Initialize-ADTModule' -ScriptDirectory '$([System.String]::Join("', '", $Script:ADT.Directories.Script.Replace("'", "''")))'; `$null = & `$CommandTable.'Show-ADTInstallationPrompt$($adtConfig.UI.DialogStyle)' -Title '$($adtSession.InstallTitle.Replace("'","''"))' -Subtitle '$([System.String]::Format($adtStrings.WelcomePrompt.Fluent.Subtitle, $adtSession.DeploymentType).Replace("'", "''"))' -Timeout $($adtConfig.UI.DefaultTimeout) -Message '$($adtStrings.BlockExecution.Message.Replace("'", "''"))' -Icon Warning -ButtonRightText OK }"
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key $blockExecRegPath -Name BlockExecutionCommand -Value $blockExecCommand
+
+ # Enumerate each process and set the debugger value to block application execution.
+ foreach ($process in ($ProcessName -replace '$', '.exe'))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Setting the Image File Execution Option registry key to block execution of [$process]."
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key (& $Script:CommandTable.'Join-Path' -Path 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options' -ChildPath $process) -Name Debugger -Value "conhost.exe --headless $([System.IO.Path]::GetFileName($adtEnv.envPSProcessPath)) $(if (!(& $Script:CommandTable.'Test-ADTModuleIsReleaseBuild')) { "-ExecutionPolicy Bypass " })-NonInteractive -NoProfile -Command & ([scriptblock]::Create([Microsoft.Win32.Registry]::GetValue('$($blockExecRegPath -replace '^Microsoft\.PowerShell\.Core\\Registry::')', 'BlockExecutionCommand', `$null))); #"
+ }
+
+ # Add callback to remove all blocked app executions during the shutdown of the final session.
+ & $Script:CommandTable.'Add-ADTSessionFinishingCallback' -Callback $Script:CommandTable.'Unblock-ADTAppExecution'
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Close-ADTInstallationProgress
+#
+#-----------------------------------------------------------------------------
+
+function Close-ADTInstallationProgress
+{
+ <#
+ .SYNOPSIS
+ Closes the dialog created by Show-ADTInstallationProgress.
+
+ .DESCRIPTION
+ Closes the dialog created by Show-ADTInstallationProgress. This function is called by the Close-ADTSession function to close a running instance of the progress dialog if found.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Close-ADTInstallationProgress
+
+ This example closes the dialog created by Show-ADTInstallationProgress.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Close-ADTInstallationProgress
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Return early if we're silent, a window wouldn't have ever opened.
+ if (!(& $Script:CommandTable.'Test-ADTInstallationProgressRunning'))
+ {
+ return
+ }
+ if ($adtSession -and $adtSession.IsSilent())
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing $($MyInvocation.MyCommand.Name) [Mode: $($adtSession.DeployMode)]"
+ return
+ }
+
+ # Call the underlying function to close the progress window.
+ & $Script:CommandTable."$($MyInvocation.MyCommand.Name)$($adtConfig.UI.DialogStyle)"
+ & $Script:CommandTable.'Remove-ADTSessionFinishingCallback' -Callback $MyInvocation.MyCommand
+
+ # We only send balloon tips when a session is active.
+ if (!$adtSession)
+ {
+ return
+ }
+
+ # Send out the final toast notification.
+ switch ($adtSession.GetDeploymentStatus())
+ {
+ ([PSADT.Module.DeploymentStatus]::FastRetry)
+ {
+ & $Script:CommandTable.'Show-ADTBalloonTip' -BalloonTipIcon Warning -BalloonTipText "$($adtSession.GetDeploymentTypeName()) $((& $Script:CommandTable.'Get-ADTStringTable').BalloonText.($_.ToString()))"
+ break
+ }
+ ([PSADT.Module.DeploymentStatus]::Error)
+ {
+ & $Script:CommandTable.'Show-ADTBalloonTip' -BalloonTipIcon Error -BalloonTipText "$($adtSession.GetDeploymentTypeName()) $((& $Script:CommandTable.'Get-ADTStringTable').BalloonText.($_.ToString()))"
+ break
+ }
+ default
+ {
+ & $Script:CommandTable.'Show-ADTBalloonTip' -BalloonTipIcon Info -BalloonTipText "$($adtSession.GetDeploymentTypeName()) $((& $Script:CommandTable.'Get-ADTStringTable').BalloonText.($_.ToString()))"
+ break
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Close-ADTSession
+#
+#-----------------------------------------------------------------------------
+
+function Close-ADTSession
+{
+ <#
+ .SYNOPSIS
+ Closes the active ADT session.
+
+ .DESCRIPTION
+ The Close-ADTSession function closes the active ADT session, updates the session's exit code if provided, invokes all registered callbacks, and cleans up the session state. If this is the last session, it flags the module as uninitialized and exits the process with the last exit code.
+
+ .PARAMETER ExitCode
+ The exit code to set for the session.
+
+ .PARAMETER Force
+ Forcibly exits PowerShell upon closing of the final session.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Close-ADTSession
+
+ This example closes the active ADT session without setting an exit code.
+
+ .EXAMPLE
+ Close-ADTSession -ExitCode 0
+
+ This example closes the active ADT session and sets the exit code to 0.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Close-ADTSession
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$ExitCode,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Force
+ )
+
+ begin
+ {
+ # Get the active session and throw if we don't have it.
+ try
+ {
+ $adtSession = & $Script:CommandTable.'Get-ADTSession'
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+
+ # Make this function continue on error and ensure the caller doesn't override ErrorAction.
+ $PSBoundParameters.ErrorAction = [System.Management.Automation.ActionPreference]::SilentlyContinue
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ # Change the install phase now that we're on the way out.
+ $adtSession.InstallPhase = 'Finalization'
+
+ # Update the session's exit code with the provided value.
+ if ($PSBoundParameters.ContainsKey('ExitCode') -and (!$adtSession.GetExitCode() -or !$ExitCode.Equals(60001)))
+ {
+ $adtSession.SetExitCode($ExitCode)
+ }
+
+ # Invoke all callbacks and capture all errors.
+ $callbackErrors = foreach ($callback in $($Script:ADT.Callbacks.Closing; if ($Script:ADT.Sessions.Count.Equals(1)) { $Script:ADT.Callbacks.Finishing }))
+ {
+ try
+ {
+ try
+ {
+ & $callback
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ $_; & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failure occurred while invoking callback [$($callback.Name)]."
+ }
+ }
+
+ # Close out the active session and clean up session state.
+ try
+ {
+ try
+ {
+ $ExitCode = $adtSession.Close()
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failure occurred while closing ADTSession for [$($adtSession.InstallName)]."
+ $ExitCode = 60001
+ }
+ finally
+ {
+ $null = $Script:ADT.Sessions.Remove($adtSession)
+ }
+
+ # Hand over to our backend closure routine if this was the last session.
+ if (!$Script:ADT.Sessions.Count)
+ {
+ & $Script:CommandTable.'Exit-ADTInvocation' -ExitCode $ExitCode -NoShellExit:(!$adtSession.CanExitOnClose()) -Force:($Force -or ($Host.Name.Equals('ConsoleHost') -and $callbackErrors))
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Complete-ADTFunction
+#
+#-----------------------------------------------------------------------------
+
+function Complete-ADTFunction
+{
+ <#
+ .SYNOPSIS
+ Completes the execution of an ADT function.
+
+ .DESCRIPTION
+ The Complete-ADTFunction function finalizes the execution of an ADT function by writing a debug log message and restoring the original global verbosity if it was archived off.
+
+ .PARAMETER Cmdlet
+ The PSCmdlet object representing the cmdlet being completed.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Complete-ADTFunction -Cmdlet $PSCmdlet
+
+ This example completes the execution of the current ADT function.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Complete-ADTFunction
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.PSCmdlet]$Cmdlet
+ )
+
+ # Write debug log messages and restore original global verbosity if a value was archived off.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Function End' -Source $Cmdlet.MyInvocation.MyCommand.Name -DebugMessage
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Convert-ADTRegistryPath
+#
+#-----------------------------------------------------------------------------
+
+function Convert-ADTRegistryPath
+{
+ <#
+ .SYNOPSIS
+ Converts the specified registry key path to a format that is compatible with built-in PowerShell cmdlets.
+
+ .DESCRIPTION
+ Converts the specified registry key path to a format that is compatible with built-in PowerShell cmdlets.
+
+ Converts registry key hives to their full paths. Example: HKLM is converted to "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE".
+
+ .PARAMETER Key
+ Path to the registry key to convert (can be a registry hive or fully qualified path)
+
+ .PARAMETER Wow6432Node
+ Specifies that the 32-bit registry view (Wow6432Node) should be used on a 64-bit system.
+
+ .PARAMETER SID
+ The security identifier (SID) for a user. Specifying this parameter will convert a HKEY_CURRENT_USER registry key to the HKEY_USERS\$SID format.
+
+ Specify this parameter from the Invoke-ADTAllUsersRegistryAction function to read/edit HKCU registry settings for all users on the system.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the converted registry key path.
+
+ .EXAMPLE
+ Convert-ADTRegistryPath -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1AD147D0-BE0E-3D6C-AC11-64F6DC4163F1}'
+
+ Converts the specified registry key path to a format compatible with PowerShell cmdlets.
+
+ .EXAMPLE
+ Convert-ADTRegistryPath -Key 'HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1AD147D0-BE0E-3D6C-AC11-64F6DC4163F1}'
+
+ Converts the specified registry key path to a format compatible with PowerShell cmdlets.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Convert-ADTRegistryPath
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Suppress logging output unless the caller has said otherwise.
+ if (!$PSBoundParameters.ContainsKey('InformationAction'))
+ {
+ $InformationPreference = [System.Management.Automation.ActionPreference]::SilentlyContinue
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Convert the registry key hive to the full path, only match if at the beginning of the line.
+ $Script:Registry.PathReplacements.GetEnumerator() | . {
+ process
+ {
+ if ($Key -match $_.Key)
+ {
+ foreach ($regexMatch in ($Script:Registry.PathMatches -replace '^', $_.Key))
+ {
+ $Key = $Key -replace $regexMatch, $_.Value
+ }
+ }
+ }
+ }
+
+ # Process the WOW6432Node values if applicable.
+ if ($Wow6432Node -and [System.Environment]::Is64BitProcess)
+ {
+ $Script:Registry.WOW64Replacements.GetEnumerator() | . {
+ process
+ {
+ if ($Key -match $_.Key)
+ {
+ $Key = $Key -replace $_.Key, $_.Value
+ }
+ }
+ }
+ }
+
+ # Append the PowerShell provider to the registry key path.
+ if ($Key -notmatch '^Microsoft\.PowerShell\.Core\\Registry::')
+ {
+ $Key = "Microsoft.PowerShell.Core\Registry::$key"
+ }
+
+ # If the SID variable is specified, then convert all HKEY_CURRENT_USER key's to HKEY_USERS\$SID.
+ if ($PSBoundParameters.ContainsKey('SID'))
+ {
+ if ($Key -match '^Microsoft\.PowerShell\.Core\\Registry::HKEY_CURRENT_USER\\')
+ {
+ $Key = $Key -replace '^Microsoft\.PowerShell\.Core\\Registry::HKEY_CURRENT_USER\\', "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$SID\"
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'SID parameter specified but the registry hive of the key is not HKEY_CURRENT_USER.' -Severity 2
+ return
+ }
+ }
+
+ # Check for expected key string format.
+ if ($Key -notmatch '^Microsoft\.PowerShell\.Core\\Registry::HKEY_(LOCAL_MACHINE|CLASSES_ROOT|CURRENT_USER|USERS|CURRENT_CONFIG|PERFORMANCE_DATA)')
+ {
+ $naerParams = @{
+ Exception = [System.ArgumentException]::new("Unable to detect target registry hive in string [$Key].")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'RegistryKeyValueInvalid'
+ TargetObject = $Key
+ RecommendedAction = "Please confirm the supplied value is correct and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Return fully qualified registry key path [$Key]."
+ return $Key
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Convert-ADTValuesFromRemainingArguments
+#
+#-----------------------------------------------------------------------------
+
+function Convert-ADTValuesFromRemainingArguments
+{
+ <#
+ .SYNOPSIS
+ Converts the collected values from a ValueFromRemainingArguments parameter value into a dictionary or PowerShell.exe command line arguments.
+
+ .DESCRIPTION
+ This function converts the collected values from a ValueFromRemainingArguments parameter value into a dictionary or PowerShell.exe command line arguments.
+
+ .PARAMETER RemainingArguments
+ The collected values to enumerate and process into a dictionary.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.Generic.Dictionary[System.String, System.Object]
+
+ Convert-ADTValuesFromRemainingArguments returns a dictionary of the processed input.
+
+ .EXAMPLE
+ Convert-ADTValuesFromRemainingArguments -RemainingArguments $args
+
+ Converts an $args array into a $PSBoundParameters-compatible dictionary.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Convert-ADTValuesFromRemainingArguments
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ [OutputType([System.Collections.Generic.Dictionary[System.String, System.Object]])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [AllowNull()][AllowEmptyCollection()]
+ [System.Collections.Generic.List[System.Object]]$RemainingArguments
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Process input into a dictionary and return it. Assume anything starting with a '-' is a new variable.
+ return [PSADT.Shared.Utility]::ConvertValuesFromRemainingArguments($RemainingArguments)
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Convert-ADTValueType
+#
+#-----------------------------------------------------------------------------
+
+function Convert-ADTValueType
+{
+ <#
+ .SYNOPSIS
+ Casts the provided value to the requested type without range errors.
+
+ .DESCRIPTION
+ This function uses C# code to cast the provided value to the requested type. This avoids errors from PowerShell when values exceed the casted value type's range.
+
+ .PARAMETER Value
+ The value to convert.
+
+ .PARAMETER To
+ What to cast the value to.
+
+ .INPUTS
+ System.Int64
+
+ Convert-ADTValueType will accept any value type as a signed 64-bit integer, then cast to the requested type.
+
+ .OUTPUTS
+ System.ValueType
+
+ Convert-ADTValueType will convert the piped input to this type if specified by the caller.
+
+ .EXAMPLE
+ Convert-ADTValueType -Value 256 -To SByte
+
+ Invokes the Convert-ADTValueType function and returns the value as a byte, which would equal 0.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Convert-ADTValueType
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int64]$Value,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Shared.ValueTypes]$To
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $method = "To$To"
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Use our custom converter to get it done.
+ return [PSADT.Shared.ValueTypeConverter]::$method($Value)
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: ConvertTo-ADTNTAccountOrSID
+#
+#-----------------------------------------------------------------------------
+
+function ConvertTo-ADTNTAccountOrSID
+{
+ <#
+
+ .SYNOPSIS
+ Convert between NT Account names and their security identifiers (SIDs).
+
+ .DESCRIPTION
+ Specify either the NT Account name or the SID and get the other. Can also convert well known sid types.
+
+ .PARAMETER AccountName
+ The Windows NT Account name specified in \ format.
+
+ Use fully qualified account names (e.g., \) instead of isolated names (e.g, ) because they are unambiguous and provide better performance.
+
+ .PARAMETER SID
+ The Windows NT Account SID.
+
+ .PARAMETER WellKnownSIDName
+ Specify the Well Known SID name translate to the actual SID (e.g., LocalServiceSid).
+
+ To get all well known SIDs available on system: [Enum]::GetNames([Security.Principal.WellKnownSidType])
+
+ .PARAMETER WellKnownToNTAccount
+ Convert the Well Known SID to an NTAccount name.
+
+ .PARAMETER LocalHost
+ Avoids a costly domain check when only converting local accounts.
+
+ .INPUTS
+ System.String
+
+ Accepts a string containing the NT Account name or SID.
+
+ .OUTPUTS
+ System.String
+
+ Returns the NT Account name or SID.
+
+ .EXAMPLE
+ ConvertTo-ADTNTAccountOrSID -AccountName 'CONTOSO\User1'
+
+ Converts a Windows NT Account name to the corresponding SID.
+
+ .EXAMPLE
+ ConvertTo-ADTNTAccountOrSID -SID 'S-1-5-21-1220945662-2111687655-725345543-14012660'
+
+ Converts a Windows NT Account SID to the corresponding NT Account Name.
+
+ .EXAMPLE
+ ConvertTo-ADTNTAccountOrSID -WellKnownSIDName 'NetworkServiceSid'
+
+ Converts a Well Known SID name to a SID.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ The conversion can return an empty result if the user account does not exist anymore or if translation fails Refer to: http://blogs.technet.com/b/askds/archive/2011/07/28/troubleshooting-sid-translation-failures-from-the-obvious-to-the-not-so-obvious.aspx
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/ConvertTo-ADTNTAccountOrSID
+
+ .LINK
+ http://msdn.microsoft.com/en-us/library/system.security.principal.wellknownsidtype(v=vs.110).aspx
+
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Security.Principal.SecurityIdentifier])]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'NTAccountToSID', ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.Principal.NTAccount]$AccountName,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SIDToNTAccount', ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.Principal.SecurityIdentifier]$SID,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'WellKnownName', ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.Principal.WellKnownSidType]$WellKnownSIDName,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'WellKnownName')]
+ [System.Management.Automation.SwitchParameter]$WellKnownToNTAccount,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'WellKnownName')]
+ [System.Management.Automation.SwitchParameter]$LocalHost
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Pre-calculate the domain SID.
+ $DomainSid = if ($PSCmdlet.ParameterSetName.Equals('WellKnownName') -and !$LocalHost)
+ {
+ try
+ {
+ [System.Security.Principal.SecurityIdentifier]::new([System.DirectoryServices.DirectoryEntry]::new("LDAP://$((& $Script:CommandTable.'Get-CimInstance' -ClassName Win32_ComputerSystem).Domain.ToLower())").ObjectSid[0], 0)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Unable to get Domain SID from Active Directory. Setting Domain SID to $null.' -Severity 2
+ }
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ switch ($PSCmdlet.ParameterSetName)
+ {
+ SIDToNTAccount
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Converting $(($msg = "the SID [$SID] to an NT Account name"))."
+ return $SID.Translate([System.Security.Principal.NTAccount])
+ }
+ NTAccountToSID
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Converting $(($msg = "the NT Account [$AccountName] to a SID"))."
+ return $AccountName.Translate([System.Security.Principal.SecurityIdentifier])
+ }
+ WellKnownName
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Converting $(($msg = "the Well Known SID Name [$WellKnownSIDName] to a $(('SID', 'NTAccount')[!!$WellKnownToNTAccount])"))."
+ $NTAccountSID = [System.Security.Principal.SecurityIdentifier]::new($WellKnownSIDName, $DomainSid)
+ if ($WellKnownToNTAccount)
+ {
+ return $NTAccountSID.Translate([System.Security.Principal.NTAccount])
+ }
+ return $NTAccountSID
+ }
+ }
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to convert $msg. It may not be a valid account anymore or there is some other problem."
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Copy-ADTContentToCache
+#
+#-----------------------------------------------------------------------------
+
+function Copy-ADTContentToCache
+{
+ <#
+ .SYNOPSIS
+ Copies the toolkit content to a cache folder on the local machine and sets the $adtSession.DirFiles and $adtSession.DirSupportFiles directory to the cache path.
+
+ .DESCRIPTION
+ Copies the toolkit content to a cache folder on the local machine and sets the $adtSession.DirFiles and $adtSession.DirSupportFiles directory to the cache path.
+
+ This function is useful in environments where an Endpoint Management solution does not provide a managed cache for source files, such as Intune.
+
+ It is important to clean up the cache in the uninstall section for the current version and potentially also in the pre-installation section for previous versions.
+
+ .PARAMETER Path
+ The path to the software cache folder.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Copy-ADTContentToCache -Path "$envWinDir\Temp\PSAppDeployToolkit"
+
+ This example copies the toolkit content to the specified cache folder.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ This can be used in the absence of an Endpoint Management solution that provides a managed cache for source files, e.g. Intune is lacking this functionality whereas ConfigMgr includes this functionality.
+
+ Since this cache folder is effectively unmanaged, it is important to cleanup the cache in the uninstall section for the current version and potentially also in the pre-installation section for previous versions.
+
+ This can be done using `Remove-ADTFile -Path "(Get-ADTConfig).Toolkit.CachePath\$($adtSession.InstallName)" -Recurse -ErrorAction Ignore`.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Copy-ADTContentToCache
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path = "$((& $Script:CommandTable.'Get-ADTConfig').Toolkit.CachePath)\$((& $Script:CommandTable.'Get-ADTSession').InstallName)"
+ )
+
+ begin
+ {
+ try
+ {
+ $adtSession = & $Script:CommandTable.'Get-ADTSession'
+ $scriptDir = & $Script:CommandTable.'Get-ADTSessionCacheScriptDirectory'
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ # Create the cache folder if it does not exist.
+ if (![System.IO.Directory]::Exists($Path))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating cache folder [$Path]."
+ try
+ {
+ try
+ {
+ $null = & $Script:CommandTable.'New-Item' -Path $Path -ItemType Directory
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to create cache folder [$Path]."
+ return
+ }
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Cache folder [$Path] already exists."
+ }
+
+ # Copy the toolkit content to the cache folder.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying toolkit content to cache folder [$Path]."
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Copy-ADTFile' -Path ([System.IO.Path]::Combine($scriptDir, '*')) -Destination $Path -Recurse
+ $adtSession.DirFiles = [System.IO.Path]::Combine($scriptDir, 'Files')
+ $adtSession.DirSupportFiles = [System.IO.Path]::Combine($scriptDir, 'SupportFiles')
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to copy toolkit content to cache folder [$Path]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Copy-ADTFile
+#
+#-----------------------------------------------------------------------------
+
+function Copy-ADTFile
+{
+ <#
+ .SYNOPSIS
+ Copies files and directories from a source to a destination.
+
+ .DESCRIPTION
+ Copies files and directories from a source to a destination. This function supports recursive copying, overwriting existing files, and returning the copied items.
+
+ .PARAMETER Path
+ Path of the file to copy. Multiple paths can be specified.
+
+ .PARAMETER Destination
+ Destination Path of the file to copy.
+
+ .PARAMETER Recurse
+ Copy files in subdirectories.
+
+ .PARAMETER Flatten
+ Flattens the files into the root destination directory.
+
+ .PARAMETER ContinueFileCopyOnError
+ Continue copying files if an error is encountered. This will continue the deployment script and will warn about files that failed to be copied.
+
+ .PARAMETER FileCopyMode
+ Select from 'Native' or 'Robocopy'. Default is configured in config.psd1. Note that Robocopy supports * in file names, but not folders, in source paths.
+
+ .PARAMETER RobocopyParams
+ Override the default Robocopy parameters.
+
+ .PARAMETER RobocopyAdditionalParams
+ Append to the default Robocopy parameters.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Copy-ADTFile -Path 'C:\Path\file.txt' -Destination 'D:\Destination\file.txt'
+
+ Copies the file 'file.txt' from 'C:\Path' to 'D:\Destination'.
+
+ .EXAMPLE
+ Copy-ADTFile -Path 'C:\Path\Folder' -Destination 'D:\Destination\Folder' -Recurse
+
+ Recursively copies the folder 'Folder' from 'C:\Path' to 'D:\Destination'.
+
+ .EXAMPLE
+ Copy-ADTFile -Path 'C:\Path\file.txt' -Destination 'D:\Destination\file.txt'
+
+ Copies the file 'file.txt' from 'C:\Path' to 'D:\Destination', overwriting the destination file if it exists.
+
+ .EXAMPLE
+ Copy-ADTFile -Path "$($adtSession.DirFiles)\*" -Destination C:\some\random\file\path
+
+ Copies all files within the active session's Files folder to 'C:\some\random\file\path', overwriting the destination file if it exists.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Copy-ADTFile
+ #>
+
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Destination,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Flatten,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ContinueFileCopyOnError,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Native', 'Robocopy')]
+ [System.String]$FileCopyMode,
+
+ [Parameter(Mandatory = $false)]
+ [System.String]$RobocopyParams = '/NJH /NJS /NS /NC /NP /NDL /FP /IS /IT /IM /XX /MT:4 /R:1 /W:1',
+
+ [Parameter(Mandatory = $false)]
+ [System.String]$RobocopyAdditionalParams
+
+ )
+
+ begin
+ {
+ # If a FileCopyMode hasn't been specified, potentially initialize the module so we can get it from the config.
+ if (!$PSBoundParameters.ContainsKey('FileCopyMode'))
+ {
+ $null = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ $FileCopyMode = (& $Script:CommandTable.'Get-ADTConfig').Toolkit.FileCopyMode
+ }
+
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Verify that Robocopy can be used if selected
+ if ($FileCopyMode -eq 'Robocopy')
+ {
+ # Check if Robocopy is on the system.
+ if (& $Script:CommandTable.'Test-Path' -Path "$([System.Environment]::SystemDirectory)\Robocopy.exe" -PathType Leaf)
+ {
+ # Disable Robocopy if $Path has a folder containing a * wildcard.
+ if ($Path -match '\*.*\\')
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Asterisk wildcard specified in folder portion of path variable. Falling back to native PowerShell method." -Severity 2
+ $FileCopyMode = 'Native'
+ }
+ # Don't just check for an extension here, also check for base name without extension to allow copying to a directory such as .config.
+ elseif ([System.IO.Path]::HasExtension($Destination) -and [System.IO.Path]::GetFileNameWithoutExtension($Destination) -and !(& $Script:CommandTable.'Test-Path' -LiteralPath $Destination -PathType Container))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Destination path appears to be a file. Falling back to native PowerShell method." -Severity 2
+ $FileCopyMode = 'Native'
+ }
+ else
+ {
+ $robocopyCommand = "$([System.Environment]::SystemDirectory)\Robocopy.exe"
+
+ if ($Recurse -and !$Flatten)
+ {
+ # Add /E to Robocopy parameters if it is not already included.
+ if ($RobocopyParams -notmatch '/E(\s+|$)' -and $RobocopyAdditionalParams -notmatch '/E(\s+|$)')
+ {
+ $RobocopyParams = $RobocopyParams + " /E"
+ }
+ }
+ else
+ {
+ # Ensure that /E is not included in the Robocopy parameters as it will copy recursive folders.
+ $RobocopyParams = $RobocopyParams -replace '/E(\s+|$)'
+ $RobocopyAdditionalParams = $RobocopyAdditionalParams -replace '/E(\s+|$)'
+ }
+
+ # Older versions of Robocopy do not support /IM, remove if unsupported.
+ if ((& $robocopyCommand /?) -notmatch '/IM\s')
+ {
+ $RobocopyParams = $RobocopyParams -replace '/IM(\s+|$)'
+ $RobocopyAdditionalParams = $RobocopyAdditionalParams -replace '/IM(\s+|$)'
+ }
+ }
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy is not available on this system. Falling back to native PowerShell method." -Severity 2
+ $FileCopyMode = 'Native'
+ }
+ }
+ }
+
+ process
+ {
+ if ($FileCopyMode -eq 'Robocopy')
+ {
+ foreach ($srcPath in $Path)
+ {
+ try
+ {
+ if (!(& $Script:CommandTable.'Test-Path' -Path $srcPath))
+ {
+ if (!$ContinueFileCopyOnError)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Source path [$srcPath] not found." -Severity 2
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("Source path [$srcPath] not found.")
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'FileNotFoundError'
+ TargetObject = $srcPath
+ RecommendedAction = 'Please verify that the path is accessible and try again.'
+ }
+ & $Script:CommandTable.'Write-Error' -ErrorRecord (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Source path [$srcPath] not found. Will continue due to ContinueFileCopyOnError = `$true." -Severity 2
+ continue
+ }
+
+ # Pre-create destination folder if it does not exist; Robocopy will auto-create non-existent destination folders, but pre-creating ensures we can use Resolve-Path.
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $Destination -PathType Container))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Destination assumed to be a folder which does not exist, creating destination folder [$Destination]."
+ $null = & $Script:CommandTable.'New-Item' -Path $Destination -Type Directory -Force
+ }
+
+ # If source exists as a folder, append the last subfolder to the destination, so that Robocopy produces similar results to native PowerShell.
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath $srcPath -PathType Container)
+ {
+ # Trim ending backslash from paths which can cause problems with Robocopy.
+ # Resolve paths in case relative paths beggining with .\, ..\, or \ are used.
+ # Strip Microsoft.PowerShell.Core\FileSystem:: from the beginning of the resulting string, since Resolve-Path adds this to UNC paths.
+ $robocopySource = (& $Script:CommandTable.'Resolve-Path' -LiteralPath $srcPath.TrimEnd('\')).Path -replace '^Microsoft\.PowerShell\.Core\\FileSystem::'
+ $robocopyDestination = & $Script:CommandTable.'Join-Path' ((& $Script:CommandTable.'Resolve-Path' -LiteralPath $Destination).Path -replace '^Microsoft\.PowerShell\.Core\\FileSystem::') (& $Script:CommandTable.'Split-Path' -Path $srcPath -Leaf)
+ $robocopyFile = '*'
+ }
+ else
+ {
+ # Else assume source is a file and split args to the format .
+ # Trim ending backslash from paths which can cause problems with Robocopy.
+ # Resolve paths in case relative paths beggining with .\, ..\, or \ are used.
+ # Strip Microsoft.PowerShell.Core\FileSystem:: from the beginning of the resulting string, since Resolve-Path adds this to UNC paths.
+ $ParentPath = & $Script:CommandTable.'Split-Path' -Path $srcPath -Parent
+ $robocopySource = if ([System.String]::IsNullOrWhiteSpace($ParentPath))
+ {
+ $ExecutionContext.SessionState.Path.CurrentLocation.Path
+ }
+ else
+ {
+ (& $Script:CommandTable.'Resolve-Path' -LiteralPath $ParentPath).Path -replace '^Microsoft\.PowerShell\.Core\\FileSystem::'
+ }
+ $robocopyDestination = (& $Script:CommandTable.'Resolve-Path' -LiteralPath $Destination.TrimEnd('\')).Path -replace '^Microsoft\.PowerShell\.Core\\FileSystem::'
+ $robocopyFile = (& $Script:CommandTable.'Split-Path' -Path $srcPath -Leaf)
+ }
+
+ # Set up copy operation.
+ if ($Flatten)
+ {
+ # Copy all files from the root source folder.
+ $copyFileSplat = @{
+ Destination = $Destination # Use the original destination path, not $robocopyDestination which could have had a subfolder appended to it.
+ Recurse = $false # Disable recursion as this will create subfolders in the destination.
+ Flatten = $false # Disable flattening to prevent infinite loops.
+ ContinueFileCopyOnError = $ContinueFileCopyOnError
+ FileCopyMode = $FileCopyMode
+ RobocopyParams = $RobocopyParams
+ RobocopyAdditionalParams = $RobocopyAdditionalParams
+ }
+ if ($PSBoundParameters.ContainsKey('ErrorAction'))
+ {
+ $copyFileSplat.ErrorAction = $PSBoundParameters.ErrorAction
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying file(s) recursively in path [$srcPath] to destination [$Destination] root folder, flattened."
+ & $Script:CommandTable.'Copy-ADTFile' @copyFileSplat -Path ((& $Script:CommandTable.'Join-Path' $robocopySource $robocopyFile))
+
+ # Copy all files from subfolders, appending file name to subfolder path and repeat Copy-ADTFile.
+ & $Script:CommandTable.'Get-ChildItem' -Path $robocopySource -Directory -Recurse -Force -ErrorAction Ignore | & {
+ process
+ {
+ & $Script:CommandTable.'Copy-ADTFile' @copyFileSplat -Path (& $Script:CommandTable.'Join-Path' $_.FullName $robocopyFile)
+ }
+ }
+
+ # Skip to next $srcPath in $Path since we have handed off all copy tasks to separate executions of the function.
+ continue
+ }
+ elseif ($Recurse)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying file(s) recursively in path [$srcPath] to destination [$Destination]."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying file(s) in path [$srcPath] to destination [$Destination]."
+ }
+
+ # Create new directory if it doesn't exist.
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $robocopyDestination -PathType Container))
+ {
+ $null = & $Script:CommandTable.'New-Item' -Path $robocopyDestination -Type Directory -Force
+ }
+
+ # Backup destination folder attributes in case known Robocopy bug overwrites them.
+ $destFolderAttributes = [System.IO.File]::GetAttributes($robocopyDestination)
+
+ # Begin copy operation.
+ $robocopyArgs = "`"$robocopySource`" `"$robocopyDestination`" `"$robocopyFile`" $RobocopyParams $RobocopyAdditionalParams"
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Executing Robocopy command: $robocopyCommand $robocopyArgs"
+ $robocopyResult = & $Script:CommandTable.'Start-ADTProcess' -FilePath $robocopyCommand -ArgumentList $robocopyArgs -CreateNoWindow -PassThru -SuccessExitCodes 0, 1, 2, 3, 4, 5, 6, 7, 8 -ErrorAction Ignore
+
+ # Trim the last line plus leading whitespace from each line of Robocopy output.
+ $robocopyOutput = $robocopyResult.StdOut.Trim() -Replace '\n\s+', "`n"
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy output:`n$robocopyOutput"
+
+ # Restore folder attributes in case Robocopy overwrote them.
+ try
+ {
+ [System.IO.File]::SetAttributes($robocopyDestination, $destFolderAttributes)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to apply attributes [$destFolderAttributes] destination folder [$robocopyDestination]: $($_.Exception.Message)" -Severity 2
+ }
+
+ # Process the resulting exit code.
+ switch ($robocopyResult.ExitCode)
+ {
+ 0 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. No files were copied. No failure was encountered. No files were mismatched. The files already exist in the destination directory; therefore, the copy operation was skipped."; break }
+ 1 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. All files were copied successfully."; break }
+ 2 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. There are some additional files in the destination directory that aren't present in the source directory. No files were copied."; break }
+ 3 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. Some files were copied. Additional files were present. No failure was encountered."; break }
+ 4 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. Some Mismatched files or directories were detected. Examine the output log. Housekeeping might be required." -Severity 2; break }
+ 5 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. Some files were copied. Some files were mismatched. No failure was encountered."; break }
+ 6 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. Additional files and mismatched files exist. No files were copied and no failures were encountered meaning that the files already exist in the destination directory." -Severity 2; break }
+ 7 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. Files were copied, a file mismatch was present, and additional files were present." -Severity 2; break }
+ 8 { & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy completed. Several files didn't copy." -Severity 2; break }
+ 16
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy error [$($robocopyResult.ExitCode)]: Serious error. Robocopy did not copy any files. Either a usage error or an error due to insufficient access privileges on the source or destination directories." -Severity 3
+ if (!$ContinueFileCopyOnError)
+ {
+ $naerParams = @{
+ Exception = [System.Management.Automation.ApplicationFailedException]::new("Robocopy error $($robocopyResult.ExitCode): Failed to copy file(s) in path [$srcPath] to destination [$Destination]: $robocopyOutput")
+ Category = [System.Management.Automation.ErrorCategory]::OperationStopped
+ ErrorId = 'RobocopyError'
+ TargetObject = $srcPath
+ RecommendedAction = "Please verify that Path and Destination are accessible and try again."
+ }
+ & $Script:CommandTable.'Write-Error' -ErrorRecord (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ break
+ }
+ default
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Robocopy error [$($robocopyResult.ExitCode)]. Unknown Robocopy error." -Severity 3
+ if (!$ContinueFileCopyOnError)
+ {
+ $naerParams = @{
+ Exception = [System.Management.Automation.ApplicationFailedException]::new("Robocopy error $($robocopyResult.ExitCode): Failed to copy file(s) in path [$srcPath] to destination [$Destination]: $robocopyOutput")
+ Category = [System.Management.Automation.ErrorCategory]::OperationStopped
+ ErrorId = 'RobocopyError'
+ TargetObject = $srcPath
+ RecommendedAction = "Please verify that Path and Destination are accessible and try again."
+ }
+ & $Script:CommandTable.'Write-Error' -ErrorRecord (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ break
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to copy file(s) in path [$srcPath] to destination [$Destination]."
+ if (!$ContinueFileCopyOnError)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'ContinueFileCopyOnError not specified, exiting function.'
+ return
+ }
+ }
+ }
+ }
+ elseif ($FileCopyMode -eq 'Native')
+ {
+ foreach ($srcPath in $Path)
+ {
+ try
+ {
+ try
+ {
+ # If destination has no extension, or if it has an extension only and no name (e.g. a .config folder) and the destination folder does not exist.
+ if ((![System.IO.Path]::HasExtension($Destination) -or ([System.IO.Path]::HasExtension($Destination) -and ![System.IO.Path]::GetFileNameWithoutExtension($Destination))) -and !(& $Script:CommandTable.'Test-Path' -LiteralPath $Destination -PathType Container))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Destination assumed to be a folder which does not exist, creating destination folder [$Destination]."
+ $null = & $Script:CommandTable.'New-Item' -Path $Destination -Type Directory -Force
+ }
+
+ # If destination appears to be a file name but parent folder does not exist, create it.
+ if ([System.IO.Path]::HasExtension($Destination) -and [System.IO.Path]::GetFileNameWithoutExtension($Destination) -and !(& $Script:CommandTable.'Test-Path' -LiteralPath ($destinationParent = & $Script:CommandTable.'Split-Path' $Destination -Parent) -PathType Container))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Destination assumed to be a file whose parent folder does not exist, creating destination folder [$destinationParent]."
+ $null = & $Script:CommandTable.'New-Item' -Path $destinationParent -Type Directory -Force
+ }
+
+ # Set up parameters for Copy-Item operation.
+ $ciParams = @{
+ Destination = $Destination
+ Force = $true
+ }
+ if ($ContinueFileCopyOnError)
+ {
+ $ciParams.Add('ErrorAction', [System.Management.Automation.ActionPreference]::SilentlyContinue)
+ $ciParams.Add('ErrorVariable', 'FileCopyError')
+ }
+
+ # Perform copy operation.
+ $null = if ($Flatten)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying file(s) recursively in path [$srcPath] to destination [$Destination] root folder, flattened."
+ if ($srcPaths = & $Script:CommandTable.'Get-ChildItem' -Path $srcPath -File -Recurse -Force -ErrorAction Ignore)
+ {
+ & $Script:CommandTable.'Copy-Item' -LiteralPath $srcPaths.PSPath @ciParams
+ }
+ }
+ elseif ($Recurse)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying file(s) recursively in path [$srcPath] to destination [$Destination]."
+ & $Script:CommandTable.'Copy-Item' -Path $srcPath -Recurse @ciParams
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying file in path [$srcPath] to destination [$Destination]."
+ & $Script:CommandTable.'Copy-Item' -Path $srcPath @ciParams
+ }
+
+ # Measure success.
+ if ($ContinueFileCopyOnError -and $FileCopyError.Count)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The following warnings were detected while copying file(s) in path [$srcPath] to destination [$Destination].`n`n$([System.String]::Join("`n", $FileCopyError.Exception.Message))" -Severity 2
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'File copy completed successfully.'
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to copy file(s) in path [$srcPath] to destination [$Destination]."
+ if (!$ContinueFileCopyOnError)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'ContinueFileCopyOnError not specified, exiting function.'
+ return
+ }
+ }
+ }
+ }
+ }
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Copy-ADTFileToUserProfiles
+#
+#-----------------------------------------------------------------------------
+
+function Copy-ADTFileToUserProfiles
+{
+ <#
+ .SYNOPSIS
+ Copy one or more items to each user profile on the system.
+
+ .DESCRIPTION
+ The Copy-ADTFileToUserProfiles function copies one or more items to each user profile on the system. It supports various options such as recursion, flattening files, and using Robocopy to overcome the 260 character limit.
+
+ .PARAMETER Path
+ The path of the file or folder to copy.
+
+ .PARAMETER Destination
+ The path of the destination folder to append to the root of the user profile.
+
+ .PARAMETER BasePath
+ The base path to append the destination folder to.
+
+ .PARAMETER Recurse
+ Copy files in subdirectories.
+
+ .PARAMETER Flatten
+ Flattens the files into the root destination directory.
+
+ .PARAMETER ContinueFileCopyOnError
+ Continue copying files if an error is encountered. This will continue the deployment script and will warn about files that failed to be copied.
+
+ .PARAMETER FileCopyMode
+ Select from 'Native' or 'Robocopy'. Default is configured in config.psd1. Note that Robocopy supports * in file names, but not folders, in source paths.
+
+ .PARAMETER RobocopyParams
+ Override the default Robocopy parameters.
+
+ .PARAMETER RobocopyAdditionalParams
+ Append to the default Robocopy parameters.
+
+ .PARAMETER ExcludeNTAccount
+ Specify NT account names in Domain\Username format to exclude from the list of user profiles.
+
+ .PARAMETER IncludeSystemProfiles
+ Include system profiles: SYSTEM, LOCAL SERVICE, NETWORK SERVICE.
+
+ .PARAMETER IncludeServiceProfiles
+ Include service profiles where NTAccount begins with NT SERVICE.
+
+ .PARAMETER ExcludeDefaultUser
+ Exclude the Default User.
+
+ .INPUTS
+ System.String[]
+
+ You can pipe in string values for $Path.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Copy-ADTFileToUserProfiles -Path "$($adtSession.DirSupportFiles)\config.txt" -Destination "AppData\Roaming\MyApp"
+
+ Copy a single file to C:\Users\\AppData\Roaming\MyApp for each user.
+
+ .EXAMPLE
+ Copy-ADTFileToUserProfiles -Path "$($adtSession.DirSupportFiles)\config.txt","$($adtSession.DirSupportFiles)\config2.txt" -Destination "AppData\Roaming\MyApp"
+
+ Copy two files to C:\Users\\AppData\Roaming\MyApp for each user.
+
+ .EXAMPLE
+ Copy-ADTFileToUserProfiles -Path "$($adtSession.DirFiles)\MyDocs" Destination "MyApp" -BasePath "Documents" -Recurse
+
+ Copy an entire folder recursively to a new MyApp folder under each user's Documents folder.
+
+ .EXAMPLE
+ Copy-ADTFileToUserProfiles -Path "$($adtSession.DirFiles)\.appConfigFolder" -Recurse
+
+ Copy an entire folder to C:\Users\ for each user.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Copy-ADTFileToUserProfiles
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param (
+ [Parameter(Mandatory = $true, Position = 1, ValueFromPipeline = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Destination,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Profile', 'AppData', 'LocalAppData', 'Desktop', 'Documents', 'StartMenu', 'Temp', 'OneDrive', 'OneDriveCommercial')]
+ [System.String]$BasePath = 'Profile',
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Flatten,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Native', 'Robocopy')]
+ [System.String]$FileCopyMode,
+
+ [Parameter(Mandatory = $false)]
+ [System.String]$RobocopyParams,
+
+ [Parameter(Mandatory = $false)]
+ [System.String]$RobocopyAdditionalParams,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ExcludeNTAccount,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$IncludeSystemProfiles,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$IncludeServiceProfiles,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$ExcludeDefaultUser,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$ContinueFileCopyOnError
+ )
+
+ begin
+ {
+ # Initalize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Define default params for Copy-ADTFile.
+ $CopyFileSplat = @{
+ Recurse = $Recurse
+ Flatten = $Flatten
+ ContinueFileCopyOnError = $ContinueFileCopyOnError
+ }
+ if ($PSBoundParameters.ContainsKey('FileCopyMode'))
+ {
+ $CopyFileSplat.FileCopyMode = $PSBoundParameters.FileCopyMode
+ }
+ if ($PSBoundParameters.ContainsKey('RobocopyParams'))
+ {
+ $CopyFileSplat.RobocopyParams = $PSBoundParameters.RobocopyParams
+ }
+ if ($PSBoundParameters.ContainsKey('RobocopyAdditionalParams'))
+ {
+ $CopyFileSplat.RobocopyAdditionalParams = $PSBoundParameters.RobocopyAdditionalParams
+ }
+ if ($PSBoundParameters.ContainsKey('ErrorAction'))
+ {
+ $CopyFileSplat.ErrorAction = $PSBoundParameters.ErrorAction
+ }
+
+ # Define default params for Get-ADTUserProfiles.
+ $GetUserProfileSplat = @{
+ IncludeSystemProfiles = $IncludeSystemProfiles
+ IncludeServiceProfiles = $IncludeServiceProfiles
+ ExcludeDefaultUser = $ExcludeDefaultUser
+ }
+ if ($ExcludeNTAccount)
+ {
+ $GetUserProfileSplat.ExcludeNTAccount = $ExcludeNTAccount
+ }
+ if ($BasePath -ne 'ProfilePath')
+ {
+ $GetUserProfileSplat.LoadProfilePaths = $true
+ }
+
+ # Collector for all provided paths.
+ $sourcePaths = [System.Collections.Specialized.StringCollection]::new()
+ }
+
+ process
+ {
+ # Add all source paths to the collection.
+ $sourcePaths.AddRange($Path)
+ }
+
+ end
+ {
+ # Copy all paths to the specified destination.
+ foreach ($UserProfile in (& $Script:CommandTable.'Get-ADTUserProfiles' @GetUserProfileSplat))
+ {
+ if ([string]::IsNullOrWhiteSpace($UserProfile."$BasePath`Path"))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Skipping user profile [$($UserProfile.NTAccount)] as path [$BasePath`Path] is not available."
+ continue
+ }
+ $dest = & $Script:CommandTable.'Join-Path' $UserProfile."$BasePath`Path" $Destination
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying path [$Path] to $($dest):"
+ & $Script:CommandTable.'Copy-ADTFile' -Path $sourcePaths -Destination $dest @CopyFileSplat
+ }
+
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Disable-ADTTerminalServerInstallMode
+#
+#-----------------------------------------------------------------------------
+
+function Disable-ADTTerminalServerInstallMode
+{
+ <#
+ .SYNOPSIS
+ Changes to user install mode for Remote Desktop Session Host/Citrix servers.
+
+ .DESCRIPTION
+ The Disable-ADTTerminalServerInstallMode function changes the server mode to user install mode for Remote Desktop Session Host/Citrix servers. This is useful for ensuring that applications are installed in a way that is compatible with multi-user environments.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Disable-ADTTerminalServerInstallMode
+
+ This example changes the server mode to user install mode for Remote Desktop Session Host/Citrix servers.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Disable-ADTTerminalServerInstallMode
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ if (!$Script:ADT.TerminalServerMode)
+ {
+ return
+ }
+
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTTerminalServerModeChange' -Mode Execute
+ $Script:ADT.TerminalServerMode = $false
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Dismount-ADTWimFile
+#
+#-----------------------------------------------------------------------------
+
+function Dismount-ADTWimFile
+{
+ <#
+ .SYNOPSIS
+ Dismounts a WIM file from the specified mount point.
+
+ .DESCRIPTION
+ The Dismount-ADTWimFile function dismounts a WIM file from the specified mount point and discards all changes. This function ensures that the specified path is a valid WIM mount point before attempting to dismount.
+
+ .PARAMETER ImagePath
+ The path to the WIM file.
+
+ .PARAMETER Path
+ The path to the WIM mount point.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Dismount-ADTWimFile -ImagePath 'C:\Path\To\File.wim'
+
+ This example dismounts the WIM file from all its mount points and discards all changes.
+
+ .EXAMPLE
+ Dismount-ADTWimFile -Path 'C:\Mount\WIM'
+
+ This example dismounts the WIM file from the specified mount point and discards all changes.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Dismount-ADTWimFile
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'ImagePath')]
+ [ValidateNotNullOrEmpty()]
+ [System.IO.FileInfo[]]$ImagePath,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.IO.DirectoryInfo[]]$Path
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ # Loop through all found mounted images.
+ foreach ($wimFile in (& $Script:CommandTable.'Get-ADTMountedWimFile' @PSBoundParameters))
+ {
+ # Announce commencement.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Dismounting WIM file at path [$($wimFile.Path)]."
+ try
+ {
+ try
+ {
+ # Perform the dismount and discard all changes.
+ try
+ {
+ $null = & $Script:CommandTable.'Invoke-ADTCommandWithRetries' -Command $Script:CommandTable.'Dismount-WindowsImage' -Path $wimFile.Path -Discard
+ }
+ catch
+ {
+ # Re-throw if this error is anything other than a file-locked error.
+ if (!$_.Exception.ErrorCode.Equals(-1052638953))
+ {
+ throw
+ }
+
+ # Get all open file handles for our path.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The directory could not be completely unmounted. Checking for any open file handles that can be closed."
+ $exeHandle = "$Script:PSScriptRoot\bin\$([PSADT.OperatingSystem.OSHelper]::GetArchitecture())\handle\handle.exe"
+ $pathRegex = "^$([System.Text.RegularExpressions.Regex]::Escape($($wimFile.Path)))"
+ $pathHandles = & $Script:CommandTable.'Get-ADTProcessHandles' | & { process { if ($_.Name -match $pathRegex) { return $_ } } }
+
+ # Throw if we have no handles to close, it means we don't know why the WIM didn't dismount.
+ if (!$pathHandles)
+ {
+ throw
+ }
+
+ # Close all open file handles.
+ foreach ($handle in $pathHandles)
+ {
+ # Close handle using handle.exe. An exit code of 0 is considered successful.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$(($msg = "Closing handle [$($handle.Handle)] for process [$($handle.Process) ($($handle.PID))]"))."
+ $handleResult = & $exeHandle -accepteula -nobanner -c $handle.Handle -p $handle.PID -y
+ if ($Global:LASTEXITCODE.Equals(0))
+ {
+ continue
+ }
+
+ # If we're here, we had a bad exit code.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message ($msg = "$msg failed with exit code [$Global:LASTEXITCODE]: $handleResult") -Severity 3
+ $naerParams = @{
+ Exception = [System.Runtime.InteropServices.ExternalException]::new($msg, $Global:LASTEXITCODE)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'HandleClosureFailure'
+ TargetObject = $handleResult
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Attempt the dismount again.
+ $null = & $Script:CommandTable.'Invoke-ADTCommandWithRetries' -Command $Script:CommandTable.'Dismount-WindowsImage' -Path $wimFile.Path -Discard
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Successfully dismounted WIM file."
+ & $Script:CommandTable.'Remove-Item' -LiteralPath $wimFile.Path -Force -Confirm:$false
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage 'Error occurred while attempting to dismount WIM file.' -ErrorAction SilentlyContinue
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Enable-ADTTerminalServerInstallMode
+#
+#-----------------------------------------------------------------------------
+
+function Enable-ADTTerminalServerInstallMode
+{
+ <#
+ .SYNOPSIS
+ Changes to user install mode for Remote Desktop Session Host/Citrix servers.
+
+ .DESCRIPTION
+ The Enable-ADTTerminalServerInstallMode function changes the server mode to user install mode for Remote Desktop Session Host/Citrix servers. This is useful for ensuring that applications are installed in a way that is compatible with multi-user environments.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Enable-ADTTerminalServerInstallMode
+
+ This example changes the server mode to user install mode for Remote Desktop Session Host/Citrix servers.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Enable-ADTTerminalServerInstallMode
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ if ($Script:ADT.TerminalServerMode)
+ {
+ return
+ }
+
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTTerminalServerModeChange' -Mode Install
+ $Script:ADT.TerminalServerMode = $true
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Export-ADTEnvironmentTableToSessionState
+#
+#-----------------------------------------------------------------------------
+
+function Export-ADTEnvironmentTableToSessionState
+{
+ <#
+ .SYNOPSIS
+ Exports the content of `Get-ADTEnvironmentTable` to the provided SessionState as variables.
+
+ .DESCRIPTION
+ This function exports the content of `Get-ADTEnvironmentTable` to the provided SessionState as variables.
+
+ .PARAMETER SessionState
+ Caller's SessionState.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Export-ADTEnvironmentTableToSessionState -SessionState $ExecutionContext.SessionState
+
+ Invokes the Export-ADTEnvironmentTableToSessionState function and exports the module's environment table to the provided SessionState.
+
+ .EXAMPLE
+ Export-ADTEnvironmentTableToSessionState -SessionState $PSCmdlet.SessionState
+
+ Invokes the Export-ADTEnvironmentTableToSessionState function and exports the module's environment table to the provided SessionState.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Export-ADTEnvironmentTableToSessionState
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SessionState]$SessionState
+ )
+
+ begin
+ {
+ # Store the environment table on the stack and initialize function.
+ try
+ {
+ $adtEnv = & $Script:CommandTable.'Get-ADTEnvironmentTable'
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ $null = $ExecutionContext.InvokeCommand.InvokeScript($SessionState, { $args[1].GetEnumerator() | . { process { & $args[0] -Name $_.Key -Value $_.Value -Option ReadOnly -Force } } $args[0] }.Ast.GetScriptBlock(), $Script:CommandTable.'New-Variable', $adtEnv)
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTApplication
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTApplication
+{
+ <#
+ .SYNOPSIS
+ Retrieves information about installed applications.
+
+ .DESCRIPTION
+ Retrieves information about installed applications by querying the registry. You can specify an application name, a product code, or both. Returns information about application publisher, name & version, product code, uninstall string, install source, location, date, and application architecture.
+
+ .PARAMETER Name
+ The name of the application to retrieve information for. Performs a contains match on the application display name by default.
+
+ .PARAMETER NameMatch
+ Specifies the type of match to perform on the application name. Valid values are 'Contains', 'Exact', 'Wildcard', and 'Regex'. The default value is 'Contains'.
+
+ .PARAMETER ProductCode
+ The product code of the application to retrieve information for.
+
+ .PARAMETER ApplicationType
+ Specifies the type of application to remove. Valid values are 'All', 'MSI', and 'EXE'. The default value is 'All'.
+
+ .PARAMETER IncludeUpdatesAndHotfixes
+ Include matches against updates and hotfixes in results.
+
+ .PARAMETER FilterScript
+ A script used to filter the results as they're processed.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.InstalledApplication
+
+ Returns a custom type with information about an installed application:
+ - UninstallKey
+ - UninstallParentKey
+ - UninstallSubKey
+ - ProductCode
+ - DisplayName
+ - DisplayVersion
+ - UninstallString
+ - QuietUninstallString
+ - InstallSource
+ - InstallLocation
+ - InstallDate
+ - Publisher
+ - SystemComponent
+ - WindowsInstaller
+ - Is64BitApplication
+
+ .EXAMPLE
+ Get-ADTApplication
+
+ This example retrieves information about all installed applications.
+
+ .EXAMPLE
+ Get-ADTApplication -Name 'Acrobat'
+
+ Returns all applications that contain the name 'Acrobat' in the DisplayName.
+
+ .EXAMPLE
+ Get-ADTApplication -Name 'Adobe Acrobat Reader' -NameMatch 'Exact'
+
+ Returns all applications that match the name 'Adobe Acrobat Reader' exactly.
+
+ .EXAMPLE
+ Get-ADTApplication -ProductCode '{AC76BA86-7AD7-1033-7B44-AC0F074E4100}'
+
+ Returns the application with the specified ProductCode.
+
+ .EXAMPLE
+ Get-ADTApplication -Name 'Acrobat' -ApplicationType 'MSI' -FilterScript { $_.Publisher -match 'Adobe' }
+
+ Returns all MSI applications that contain the name 'Acrobat' in the DisplayName and 'Adobe' in the Publisher name.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTApplication
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ProductCode', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ApplicationType', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.InstalledApplication])]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Contains', 'Exact', 'Wildcard', 'Regex')]
+ [System.String]$NameMatch = 'Contains',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Guid[]]$ProductCode,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('All', 'MSI', 'EXE')]
+ [System.String]$ApplicationType = 'All',
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeUpdatesAndHotfixes,
+
+ [Parameter(Mandatory = $false, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ScriptBlock]$FilterScript
+ )
+
+ begin
+ {
+ # Announce start.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $updatesSkippedCounter = 0
+ $uninstallKeyPaths = $(
+ 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
+ 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
+ if ([System.Environment]::Is64BitProcess)
+ {
+ 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
+ }
+ )
+
+ # If we're filtering by name, set up the relevant FilterScript.
+ $nameFilterScript = if ($Name)
+ {
+ switch ($NameMatch)
+ {
+ Contains
+ {
+ { foreach ($eachName in $Name) { if ($appRegProps.DisplayName -like "*$eachName*") { $true; break } } }
+ break
+ }
+ Exact
+ {
+ { foreach ($eachName in $Name) { if ($appRegProps.DisplayName -eq $eachName) { $true; break } } }
+ break
+ }
+ Wildcard
+ {
+ { foreach ($eachName in $Name) { if ($appRegProps.DisplayName -like $eachName) { $true; break } } }
+ break
+ }
+ Regex
+ {
+ { foreach ($eachName in $Name) { if ($appRegProps.DisplayName -match $eachName) { $true; break } } }
+ break
+ }
+ }
+ }
+ }
+
+ process
+ {
+ # Create a custom object with the desired properties for the installed applications and sanitize property details.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Getting information for installed applications$(if ($FilterScript) {' matching the provided FilterScript'})..."
+ $installedApplication = foreach ($item in (& $Script:CommandTable.'Get-ChildItem' -LiteralPath $uninstallKeyPaths -ErrorAction Ignore))
+ {
+ try
+ {
+ try
+ {
+ # Set up initial variables.
+ $appRegProps = & $Script:CommandTable.'Get-ItemProperty' -LiteralPath $item.PSPath
+ $psPropNames = $appRegProps.PSObject.Properties | & $Script:CommandTable.'Select-Object' -ExpandProperty Name
+ $defaultGuid = [System.Guid]::Empty
+
+ # Exclude anything without any properties.
+ if (!$psPropNames)
+ {
+ continue
+ }
+
+ # Exclude anything without a DisplayName field.
+ if (!$psPropNames.Contains('DisplayName') -or [System.String]::IsNullOrWhiteSpace($appRegProps.DisplayName))
+ {
+ continue
+ }
+
+ # Bypass any updates or hotfixes.
+ if (!$IncludeUpdatesAndHotfixes -and ($appRegProps.DisplayName -match '((?i)kb\d+|(Cumulative|Security) Update|Hotfix)'))
+ {
+ $updatesSkippedCounter++
+ continue
+ }
+
+ # Apply application type filter if specified.
+ $windowsInstaller = !!$(if ($psPropNames.Contains('WindowsInstaller')) { $appRegProps.WindowsInstaller })
+ if ((($ApplicationType -eq 'MSI') -and !$windowsInstaller) -or (($ApplicationType -eq 'EXE') -and $windowsInstaller))
+ {
+ continue
+ }
+
+ # Apply ProductCode filter if specified.
+ $appMsiGuid = if ($windowsInstaller -and [System.Guid]::TryParse($appRegProps.PSChildName, [ref]$defaultGuid)) { $defaultGuid }
+ if ($ProductCode -and (!$appMsiGuid -or ($ProductCode -notcontains $appMsiGuid)))
+ {
+ continue
+ }
+
+ # Apply name filter if specified.
+ if ($nameFilterScript -and !(& $nameFilterScript))
+ {
+ continue
+ }
+
+ # Build out the app object here before we filter as the caller needs to be able to filter on the object's properties.
+ $app = [PSADT.Types.InstalledApplication]::new(
+ $appRegProps.PSPath,
+ $appRegProps.PSParentPath,
+ $appRegProps.PSChildName,
+ $appMsiGuid,
+ $appRegProps.DisplayName,
+ $(if ($psPropNames.Contains('DisplayVersion') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.DisplayVersion)) { $appRegProps.DisplayVersion }),
+ $(if ($psPropNames.Contains('UninstallString') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.UninstallString)) { $appRegProps.UninstallString }),
+ $(if ($psPropNames.Contains('QuietUninstallString') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.QuietUninstallString)) { $appRegProps.QuietUninstallString }),
+ $(if ($psPropNames.Contains('InstallSource') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.InstallSource)) { $appRegProps.InstallSource }),
+ $(if ($psPropNames.Contains('InstallLocation') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.InstallLocation)) { $appRegProps.InstallLocation }),
+ $(if ($psPropNames.Contains('InstallDate') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.InstallDate)) { $appRegProps.InstallDate }),
+ $(if ($psPropNames.Contains('Publisher') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.Publisher)) { $appRegProps.Publisher }),
+ $(if ($psPropNames.Contains('HelpLink') -and ![System.String]::IsNullOrWhiteSpace($appRegProps.HelpLink)) { $appRegProps.HelpLink }),
+ !!$(if ($psPropNames.Contains('SystemComponent')) { $appRegProps.SystemComponent }),
+ $windowsInstaller,
+ ([System.Environment]::Is64BitProcess -and ($appRegProps.PSPath -notmatch '^Microsoft\.PowerShell\.Core\\Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node'))
+ )
+
+ # Build out an object and return it to the pipeline if there's no filterscript or the filterscript returns something.
+ if (!$FilterScript -or (& $Script:CommandTable.'ForEach-Object' -InputObject $app -Process $FilterScript -ErrorAction Ignore))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Found installed application [$($app.DisplayName)]$(if ($app.DisplayVersion) {" version [$($app.DisplayVersion)]"})."
+ $app
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to process the uninstall data [$item]: $($_.Exception.Message)." -ErrorAction SilentlyContinue
+ }
+ }
+
+ # Write to log the number of entries skipped due to them being considered updates.
+ if (!$IncludeUpdatesAndHotfixes -and $updatesSkippedCounter)
+ {
+ if ($updatesSkippedCounter -eq 1)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Skipped 1 entry while searching, because it was considered a Microsoft update.'
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Skipped $UpdatesSkippedCounter entries while searching, because they were considered Microsoft updates."
+ }
+ }
+
+ # Return any accumulated apps to the caller.
+ if ($installedApplication)
+ {
+ return $installedApplication
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Found no application based on the supplied FilterScript.'
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#---------------------------------------------------------------------------
+#
+# MARK: Get-ADTBoundParametersAndDefaultValues
+#
+#---------------------------------------------------------------------------
+
+function Get-ADTBoundParametersAndDefaultValues
+{
+ <#
+ .SYNOPSIS
+ Returns a hashtable with the output of $PSBoundParameters and default-valued parameters for the given InvocationInfo.
+
+ .DESCRIPTION
+ This function processes the provided InvocationInfo and combines the results of $PSBoundParameters and default-valued parameters via the InvocationInfo's ScriptBlock AST (Abstract Syntax Tree).
+
+ .PARAMETER Invocation
+ The script or function's InvocationInfo ($MyInvocation) to process.
+
+ .PARAMETER ParameterSetName
+ The ParameterSetName to use as a filter against the Invocation's parameters.
+
+ .PARAMETER HelpMessage
+ The HelpMessage field to use as a filter against the Invocation's parameters.
+
+ .PARAMETER Exclude
+ One or more parameter names to exclude from the results.
+
+ .PARAMETER CommonParameters
+ Specifies whether PowerShell advanced function common parameters should be included.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.Generic.Dictionary[System.String, System.Object]
+
+ Get-ADTBoundParametersAndDefaultValues returns a dictionary of the same base type as $PSBoundParameters for API consistency.
+
+ .EXAMPLE
+ Get-ADTBoundParametersAndDefaultValues -Invocation $MyInvocation
+
+ Returns a $PSBoundParameters-compatible dictionary with the bound parameters and any default values.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTBoundParametersAndDefaultValues
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ParameterSetName', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'HelpMessage', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Exclude', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ [OutputType([System.Collections.Generic.Dictionary[System.String, System.Object]])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.InvocationInfo]$Invocation,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ParameterSetName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$HelpMessage,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Exclude,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$CommonParameters
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Hold array of common parameters for filtration.
+ $commonParams = if (!$CommonParameters)
+ {
+ $(
+ [System.Management.Automation.PSCmdlet]::CommonParameters
+ [System.Management.Automation.PSCmdlet]::OptionalCommonParameters
+ )
+ }
+
+ # Internal function for testing parameter attributes.
+ function Test-NamedAttributeArgumentAst
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Argument', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.Language.ParameterAst]$Parameter,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Argument,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Value
+ )
+
+ # Test whether we have AttributeAst objects.
+ if (!($attributes = $Parameter.Attributes | & { process { if ($_ -is [System.Management.Automation.Language.AttributeAst]) { return $_ } } }))
+ {
+ return $false
+ }
+
+ # Test whether we have NamedAttributeArgumentAst objects.
+ if (!($namedArguments = $attributes.NamedArguments | & { process { if ($_.ArgumentName.Equals($Argument)) { return $_ } } }))
+ {
+ return $false
+ }
+
+ # Test whether any NamedAttributeArgumentAst objects match our value.
+ return $namedArguments.Argument.Value.Contains($Value)
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Get the parameters from the provided invocation. This can vary between simple/advanced functions and scripts.
+ $parameters = if ($Invocation.MyCommand.ScriptBlock.Ast -is [System.Management.Automation.Language.FunctionDefinitionAst])
+ {
+ # Test whether this is a simple or advanced function.
+ if ($Invocation.MyCommand.ScriptBlock.Ast.Parameters -and $Invocation.MyCommand.ScriptBlock.Ast.Parameters.Count)
+ {
+ $Invocation.MyCommand.ScriptBlock.Ast.Parameters
+ }
+ elseif ($Invocation.MyCommand.ScriptBlock.Ast.Body.ParamBlock -and $Invocation.MyCommand.ScriptBlock.Ast.Body.ParamBlock.Parameters.Count)
+ {
+ $Invocation.MyCommand.ScriptBlock.Ast.Body.ParamBlock.Parameters
+ }
+ }
+ elseif ($Invocation.MyCommand.ScriptBlock.Ast.ParamBlock -and $Invocation.MyCommand.ScriptBlock.Ast.ParamBlock.Parameters.Count)
+ {
+ $Invocation.MyCommand.ScriptBlock.Ast.ParamBlock.Parameters
+ }
+
+ # Throw if we don't have any parameters at all.
+ if (!$parameters -or !$parameters.Count)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Unable to find parameters within the provided invocation's scriptblock AST.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'InvocationParametersNotFound'
+ TargetObject = $Invocation.MyCommand.ScriptBlock.Ast
+ RecommendedAction = "Please verify your function or script parameter configuration and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Open dictionary to store all params and their values to return.
+ $obj = [System.Collections.Generic.Dictionary[System.String, System.Object]]::new()
+
+ # Inject our already bound parameters into above object.
+ $Invocation.BoundParameters.GetEnumerator() | & {
+ process
+ {
+ # Filter out common parameters.
+ if ($commonParams -notcontains $_.Key)
+ {
+ $obj.Add($_.Key, $_.Value)
+ }
+ }
+ }
+
+ # Build out the dictionary for returning.
+ $parameters | & {
+ process
+ {
+ # Filter out excluded values.
+ if ($Exclude -contains $_.Name.VariablePath.UserPath)
+ {
+ $null = $obj.Remove($_.Name.VariablePath.UserPath)
+ return
+ }
+
+ # Filter out values based on the specified parameter set.
+ if ($ParameterSetName -and !(Test-NamedAttributeArgumentAst -Parameter $_ -Argument ParameterSetName -Value $ParameterSetName))
+ {
+ $null = $obj.Remove($_.Name.VariablePath.UserPath)
+ return
+ }
+
+ # Filter out values based on the specified help message.
+ if ($HelpMessage -and !(Test-NamedAttributeArgumentAst -Parameter $_ -Argument HelpMessage -Value $HelpMessage))
+ {
+ $null = $obj.Remove($_.Name.VariablePath.UserPath)
+ return
+ }
+
+ # Filter out parameters already bound.
+ if ($obj.ContainsKey($_.Name.VariablePath.UserPath))
+ {
+ return
+ }
+
+ # Filter out parameters without a default value.
+ if ($null -eq $_.DefaultValue)
+ {
+ return
+ }
+
+ # Add the parameter and its value.
+ $obj.Add($_.Name.VariablePath.UserPath, $_.DefaultValue.SafeGetValue())
+ }
+ }
+
+ # Return dictionary to the caller, even if it's empty.
+ return $obj
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTCommandTable
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTCommandTable
+{
+ <#
+ .SYNOPSIS
+ Returns PSAppDeployToolkit's safe command lookup table.
+
+ .DESCRIPTION
+ This function returns PSAppDeployToolkit's safe command lookup table, which can be used for command lookups within extending modules.
+
+ Please note that PSAppDeployToolkit's safe command table only has commands in it that are used within this module, and not necessarily all commands offered by PowerShell and its built-in modules out of the box.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.ObjectModel.ReadOnlyDictionary[System.String, System.Management.Automation.CommandInfo]
+
+ Returns PSAppDeployTookit's safe command lookup table as a ReadOnlyDictionary.
+
+ .EXAMPLE
+ Get-ADTCommandTable
+
+ Returns PSAppDeployToolkit's safe command lookup table.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTCommandTable
+ #>
+
+ # Return the module's read-only CommandTable to the caller.
+ return $Script:CommandTable
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTConfig
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTConfig
+{
+ <#
+ .SYNOPSIS
+ Retrieves the configuration data for the ADT module.
+
+ .DESCRIPTION
+ The Get-ADTConfig function retrieves the configuration data for the ADT module. This function ensures that the ADT module has been initialized before attempting to retrieve the configuration data. If the module is not initialized, it throws an error.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.Hashtable
+
+ Returns the configuration data as a hashtable.
+
+ .EXAMPLE
+ $config = Get-ADTConfig
+
+ This example retrieves the configuration data for the ADT module and stores it in the $config variable.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTConfig
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ # Return the config database if initialized.
+ if (!$Script:ADT.Config -or !$Script:ADT.Config.Count)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Please ensure that [Initialize-ADTModule] is called before using any $($MyInvocation.MyCommand.Module.Name) functions.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'ADTConfigNotLoaded'
+ TargetObject = $Script:ADT.Config
+ RecommendedAction = "Please ensure the module is initialized via [Initialize-ADTModule] and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ return $Script:ADT.Config
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTDeferHistory
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTDeferHistory
+{
+ <#
+ .SYNOPSIS
+ Get the history of deferrals in the registry for the current application.
+
+ .DESCRIPTION
+ Get the history of deferrals in the registry for the current application.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Get-DeferHistory
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTDeferHistory
+
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ try
+ {
+ (& $Script:CommandTable.'Get-ADTSession').GetDeferHistory()
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTEnvironment
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTEnvironment
+{
+ <#
+ .SYNOPSIS
+ Retrieves the environment data for the ADT module. This function has been replaced by Get-ADTEnvironmentTable and will be removed from a future release.
+
+ .DESCRIPTION
+ The Get-ADTEnvironment function retrieves the environment data for the ADT module. This function ensures that the ADT module has been initialized before attempting to retrieve the environment data. If the module is not initialized, it throws an error.
+
+ This function has been replaced by Get-ADTEnvironmentTable and will be removed from a future release.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.Specialized.OrderedDictionary
+
+ Returns the environment data as a read-only ordered dictionary.
+
+ .EXAMPLE
+ $environment = Get-ADTEnvironment
+
+ This example retrieves the environment data for the ADT module and stores it in the $environment variable.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTEnvironment
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ # Announce deprecation and return the environment database if initialized.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The function [$($MyInvocation.MyCommand.Name)] has been replaced by [Get-ADTEnvironmentTable]. Please migrate your scripts as this will be removed in a future update." -Severity 2
+ return (& $Script:CommandTable.'Get-ADTEnvironmentTable')
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTEnvironmentTable
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTEnvironmentTable
+{
+ <#
+ .SYNOPSIS
+ Retrieves the environment data for the ADT module.
+
+ .DESCRIPTION
+ The Get-ADTEnvironmentTable function retrieves the environment data for the ADT module. This function ensures that the ADT module has been initialized before attempting to retrieve the environment data. If the module is not initialized, it throws an error.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.Specialized.OrderedDictionary
+
+ Returns the environment data as a read-only ordered dictionary.
+
+ .EXAMPLE
+ $environment = Get-ADTEnvironmentTable
+
+ This example retrieves the environment data for the ADT module and stores it in the $environment variable.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTEnvironmentTable
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ # Return the environment database if initialized.
+ if (!$Script:ADT.Environment -or !$Script:ADT.Environment.Count)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Please ensure that [Initialize-ADTModule] is called before using any $($MyInvocation.MyCommand.Module.Name) functions.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'ADTEnvironmentDatabaseEmpty'
+ TargetObject = $Script:ADT.Environment
+ RecommendedAction = "Please ensure the module is initialized via [Initialize-ADTModule] and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ return $Script:ADT.Environment
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTFileVersion
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTFileVersion
+{
+ <#
+ .SYNOPSIS
+ Gets the version of the specified file.
+
+ .DESCRIPTION
+ The Get-ADTFileVersion function retrieves the version information of the specified file. By default, it returns the FileVersion, but it can also return the ProductVersion if the -ProductVersion switch is specified.
+
+ .PARAMETER File
+ The path of the file.
+
+ .PARAMETER ProductVersion
+ Switch that makes the command return ProductVersion instead of FileVersion.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the version of the specified file.
+
+ .EXAMPLE
+ Get-ADTFileVersion -File "$env:ProgramFilesX86\Adobe\Reader 11.0\Reader\AcroRd32.exe"
+
+ This example retrieves the FileVersion of the specified Adobe Reader executable.
+
+ .EXAMPLE
+ Get-ADTFileVersion -File "$env:ProgramFilesX86\Adobe\Reader 11.0\Reader\AcroRd32.exe" -ProductVersion
+
+ This example retrieves the ProductVersion of the specified Adobe Reader executable.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTFileVersion
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (!$_.Exists)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName File -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist.'))
+ }
+ if (!$_.VersionInfo -or (!$_.VersionInfo.FileVersion -and !$_.VersionInfo.ProductVersion))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName File -ProvidedValue $_ -ExceptionMessage 'The specified file does not have any version info.'))
+ }
+ return !!$_.VersionInfo
+ })]
+ [System.IO.FileInfo]$File,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ProductVersion
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ if ($ProductVersion)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Product version is [$($File.VersionInfo.ProductVersion)]."
+ return $File.VersionInfo.ProductVersion.Trim()
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "File version is [$($File.VersionInfo.FileVersion)]."
+ return $File.VersionInfo.FileVersion.Trim()
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTFreeDiskSpace
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTFreeDiskSpace
+{
+ <#
+ .SYNOPSIS
+ Retrieves the free disk space in MB on a particular drive (defaults to system drive).
+
+ .DESCRIPTION
+ The Get-ADTFreeDiskSpace function retrieves the free disk space in MB on a specified drive. If no drive is specified, it defaults to the system drive. This function is useful for monitoring disk space availability.
+
+ .PARAMETER Drive
+ The drive to check free disk space on.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Double
+
+ Returns the free disk space in MB.
+
+ .EXAMPLE
+ Get-ADTFreeDiskSpace -Drive 'C:'
+
+ This example retrieves the free disk space on the C: drive.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTFreeDiskSpace
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if (!$_.TotalSize)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Drive -ProvidedValue $_ -ExceptionMessage 'The specified drive does not exist or has no media loaded.'))
+ }
+ return !!$_.TotalSize
+ })]
+ [System.IO.DriveInfo]$Drive = [System.IO.Path]::GetPathRoot([System.Environment]::SystemDirectory)
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Retrieving free disk space for drive [$Drive]."
+ $freeDiskSpace = [System.Math]::Round($Drive.AvailableFreeSpace / 1MB)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Free disk space for drive [$Drive]: [$freeDiskSpace MB]."
+ return $freeDiskSpace
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTIniValue
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTIniValue
+{
+ <#
+ .SYNOPSIS
+ Parses an INI file and returns the value of the specified section and key.
+
+ .DESCRIPTION
+ The Get-ADTIniValue function parses an INI file and returns the value of the specified section and key. This function is useful for retrieving configuration settings stored in INI files.
+
+ .PARAMETER FilePath
+ Path to the INI file.
+
+ .PARAMETER Section
+ Section within the INI file.
+
+ .PARAMETER Key
+ Key within the section of the INI file.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the value of the specified section and key.
+
+ .EXAMPLE
+ Get-ADTIniValue -FilePath "$env:ProgramFilesX86\IBM\Notes\notes.ini" -Section 'Notes' -Key 'KeyFileName'
+
+ This example retrieves the value of the 'KeyFileName' key in the 'Notes' section of the specified INI file.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTIniValue
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Section,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Reading INI Key: [Section = $Section] [Key = $Key]."
+ try
+ {
+ try
+ {
+ $iniValue = [PSADT.Configuration.IniFile]::GetSectionKeyValue($Section, $Key, $FilePath)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "INI Key Value: [Section = $Section] [Key = $Key] [Value = $iniValue]."
+ return $iniValue
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to read INI file key value."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTLoggedOnUser
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTLoggedOnUser
+{
+ <#
+ .SYNOPSIS
+ Retrieves session details for all local and RDP logged on users.
+
+ .DESCRIPTION
+ The Get-ADTLoggedOnUser function retrieves session details for all local and RDP logged on users using Win32 APIs. It provides information such as NTAccount, SID, UserName, DomainName, SessionId, SessionName, ConnectState, IsCurrentSession, IsConsoleSession, IsUserSession, IsActiveUserSession, IsRdpSession, IsLocalAdmin, LogonTime, IdleTime, DisconnectTime, ClientName, ClientProtocolType, ClientDirectory, and ClientBuildNumber.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.UserSessionInfo
+
+ Returns a custom type with information about user sessions:
+ - NTAccount
+ - SID
+ - UserName
+ - DomainName
+ - SessionId
+ - SessionName
+ - ConnectState
+ - IsCurrentSession
+ - IsConsoleSession
+ - IsUserSession
+ - IsActiveUserSession
+ - IsRdpSession
+ - IsLocalAdmin
+ - LogonTime
+ - IdleTime
+ - DisconnectTime
+ - ClientName
+ - ClientProtocolType
+ - ClientDirectory
+ - ClientBuildNumber
+
+ .EXAMPLE
+ Get-ADTLoggedOnUser
+
+ This example retrieves session details for all local and RDP logged on users.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Description of ConnectState property:
+
+ Value Description
+ ----- -----------
+ Active A user is logged on to the session.
+ ConnectQuery The session is in the process of connecting to a client.
+ Connected A client is connected to the session.
+ Disconnected The session is active, but the client has disconnected from it.
+ Down The session is down due to an error.
+ Idle The session is waiting for a client to connect.
+ Initializing The session is initializing.
+ Listening The session is listening for connections.
+ Reset The session is being reset.
+ Shadowing This session is shadowing another session.
+
+ Description of IsActiveUserSession property:
+ - If a console user exists, then that will be the active user session.
+ - If no console user exists but users are logged in, such as on terminal servers, then the first logged-in non-console user that has ConnectState either 'Active' or 'Connected' is the active user.
+
+ Description of IsRdpSession property:
+ - Gets a value indicating whether the user is associated with an RDP client session.
+
+ Description of IsLocalAdmin property:
+ - Checks whether the user is a member of the Administrators group
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTLoggedOnUser
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Collections.ObjectModel.ReadOnlyCollection[PSADT.WTSSession.CompatibilitySessionInfo]])]
+ param
+ (
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Getting session information for all logged on users.'
+ try
+ {
+ try
+ {
+ return [PSADT.WTSSession.SessionManager]::GetCompatibilitySessionInfo()
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTMsiExitCodeMessage
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTMsiExitCodeMessage
+{
+ <#
+ .SYNOPSIS
+ Get message for MSI exit code.
+
+ .DESCRIPTION
+ Get message for MSI exit code by reading it from msimsg.dll.
+
+ .PARAMETER MsiExitCode
+ MSI exit code.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the message for the MSI exit code.
+
+ .EXAMPLE
+ Get-ADTMsiExitCodeMessage -MsiExitCode 1618
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ http://msdn.microsoft.com/en-us/library/aa368542(v=vs.85).aspx
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTMsiExitCodeMessage
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$MsiExitCode
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Only return the output if we receive something from the library.
+ if (![System.String]::IsNullOrWhiteSpace(($msg = [PSADT.Installer.Msi]::GetMessageFromMsiExitCode($MsiExitCode))))
+ {
+ return $msg
+ }
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTMsiTableProperty
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTMsiTableProperty
+{
+ <#
+ .SYNOPSIS
+ Get all of the properties from a Windows Installer database table or the Summary Information stream and return as a custom object.
+
+ .DESCRIPTION
+ Use the Windows Installer object to read all of the properties from a Windows Installer database table or the Summary Information stream.
+
+ .PARAMETER Path
+ The fully qualified path to an database file. Supports .msi and .msp files.
+
+ .PARAMETER TransformPath
+ The fully qualified path to a list of MST file(s) which should be applied to the MSI file.
+
+ .PARAMETER Table
+ The name of the the MSI table from which all of the properties must be retrieved.
+
+ .PARAMETER TablePropertyNameColumnNum
+ Specify the table column number which contains the name of the properties.
+
+ .PARAMETER TablePropertyValueColumnNum
+ Specify the table column number which contains the value of the properties.
+
+ .PARAMETER GetSummaryInformation
+ Retrieves the Summary Information for the Windows Installer database.
+
+ Summary Information property descriptions: https://msdn.microsoft.com/en-us/library/aa372049(v=vs.85).aspx
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.ObjectModel.ReadOnlyDictionary`2[[System.String],[System.Object]]
+
+ Returns a readonly dictionary with the properties as key/value pairs.
+
+ .EXAMPLE
+ Get-ADTMsiTableProperty -Path 'C:\Package\AppDeploy.msi' -TransformPath 'C:\Package\AppDeploy.mst'
+
+ Retrieve all of the properties from the default 'Property' table.
+
+ .EXAMPLE
+ (Get-ADTMsiTableProperty -Path 'C:\Package\AppDeploy.msi' -TransformPath 'C:\Package\AppDeploy.mst' -Table 'Property').ProductCode
+
+ Retrieve all of the properties from the 'Property' table, then retrieves just the 'ProductCode' member.
+
+ .EXAMPLE
+ Get-ADTMsiTableProperty -Path 'C:\Package\AppDeploy.msi' -GetSummaryInformation
+
+ Retrieve the Summary Information for the Windows Installer database.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTMsiTableProperty
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'TableInfo')]
+ [OutputType([System.Collections.ObjectModel.ReadOnlyDictionary[System.String, System.Object]])]
+ [OutputType([PSADT.Types.MsiSummaryInfo])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (!(& $Script:CommandTable.'Test-Path' -Path $_ -PathType Leaf))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified path does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if (!(& $Script:CommandTable.'Test-Path' -Path $_ -PathType Leaf))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName TransformPath -ProvidedValue $_ -ExceptionMessage 'The specified path does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String[]]$TransformPath,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'TableInfo')]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = 'MSI file: "Property"; MSP file: "MsiPatchMetadata"')]
+ [System.String]$Table,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'TableInfo')]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = 'MSI file: 1; MSP file: 2')]
+ [System.Int32]$TablePropertyNameColumnNum,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'TableInfo')]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = 'MSI file: 2; MSP file: 3')]
+ [System.Int32]$TablePropertyValueColumnNum,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SummaryInfo')]
+ [System.Management.Automation.SwitchParameter]$GetSummaryInformation
+ )
+
+ begin
+ {
+ # Set default values.
+ if (!$PSBoundParameters.ContainsKey('Table'))
+ {
+ $Table = ('MsiPatchMetadata', 'Property')[[System.IO.Path]::GetExtension($Path) -eq '.msi']
+ }
+ if (!$PSBoundParameters.ContainsKey('TablePropertyNameColumnNum'))
+ {
+ $TablePropertyNameColumnNum = 2 - ([System.IO.Path]::GetExtension($Path) -eq '.msi')
+ }
+ if (!$PSBoundParameters.ContainsKey('TablePropertyValueColumnNum'))
+ {
+ $TablePropertyValueColumnNum = 3 - ([System.IO.Path]::GetExtension($Path) -eq '.msi')
+ }
+
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ if ($PSCmdlet.ParameterSetName -eq 'TableInfo')
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Reading data from Windows Installer database file [$Path] in table [$Table]."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Reading the Summary Information from the Windows Installer database file [$Path]."
+ }
+ try
+ {
+ try
+ {
+ # Create a Windows Installer object and define properties for how the MSI database is opened
+ $Installer = & $Script:CommandTable.'New-Object' -ComObject WindowsInstaller.Installer
+ $msiOpenDatabaseModeReadOnly = 0
+ $msiSuppressApplyTransformErrors = 63
+ $msiOpenDatabaseModePatchFile = 32
+ $msiOpenDatabaseMode = if (($IsMspFile = [IO.Path]::GetExtension($Path) -eq '.msp'))
+ {
+ $msiOpenDatabaseModePatchFile
+ }
+ else
+ {
+ $msiOpenDatabaseModeReadOnly
+ }
+
+ # Open database in read only mode and apply a list of transform(s).
+ $Database = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Installer -MethodName OpenDatabase -ArgumentList @($Path, $msiOpenDatabaseMode)
+ if ($TransformPath -and !$IsMspFile)
+ {
+ $null = foreach ($Transform in $TransformPath)
+ {
+ & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Database -MethodName ApplyTransform -ArgumentList @($Transform, $msiSuppressApplyTransformErrors)
+ }
+ }
+
+ # Get either the requested windows database table information or summary information.
+ if ($GetSummaryInformation)
+ {
+ # Get the SummaryInformation from the windows installer database.
+ # Summary property descriptions: https://msdn.microsoft.com/en-us/library/aa372049(v=vs.85).aspx
+ $SummaryInformation = & $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $Database -PropertyName SummaryInformation
+ return [PSADT.Types.MsiSummaryInfo]::new(
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(1)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(2)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(3)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(4)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(5)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(6)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(7)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(8)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(9)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(11)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(12)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(13)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(14)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(15)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(16)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(18)),
+ (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(19))
+ )
+ }
+
+ # Open the requested table view from the database.
+ $TableProperties = [System.Collections.Generic.Dictionary[System.String, System.Object]]::new()
+ $View = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Database -MethodName OpenView -ArgumentList @("SELECT * FROM $Table")
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $View -MethodName Execute
+
+ # Retrieve the first row from the requested table. If the first row was successfully retrieved, then save data and loop through the entire table.
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa371136(v=vs.85).aspx
+ while (($Record = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $View -MethodName Fetch))
+ {
+ $TableProperties.Add((& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $Record -PropertyName StringData -ArgumentList @($TablePropertyNameColumnNum)), (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $Record -PropertyName StringData -ArgumentList @($TablePropertyValueColumnNum)))
+ }
+
+ # Return the accumulated results. We can't use a custom object for this as we have no idea what's going to be in the properties of a given MSI.
+ # We also can't use a pscustomobject accelerator here as the MSI may have the same keys with different casing, necessitating the use of a dictionary for storage.
+ if ($TableProperties.Count)
+ {
+ return [System.Collections.ObjectModel.ReadOnlyDictionary[System.String, System.Object]]$TableProperties
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to get the MSI table [$Table]."
+ }
+ finally
+ {
+ # Release all COM objects to prevent file locks.
+ $null = foreach ($variable in (& $Script:CommandTable.'Get-Variable' -Name View, SummaryInformation, Database, Installer -ValueOnly -ErrorAction Ignore))
+ {
+ try
+ {
+ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($variable)
+ }
+ catch
+ {
+ $null
+ }
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTObjectProperty
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTObjectProperty
+{
+ <#
+ .SYNOPSIS
+ Get a property from any object.
+
+ .DESCRIPTION
+ Get a property from any object.
+
+ .PARAMETER InputObject
+ Specifies an object which has properties that can be retrieved.
+
+ .PARAMETER PropertyName
+ Specifies the name of a property to retrieve.
+
+ .PARAMETER ArgumentList
+ Argument to pass to the property being retrieved.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Object
+
+ Returns the value of the property being retrieved.
+
+ .EXAMPLE
+ Get-ADTObjectProperty -InputObject $Record -PropertyName 'StringData' -ArgumentList @(1)
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTObjectProperty
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$InputObject,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$PropertyName,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object[]]$ArgumentList
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ return $InputObject.GetType().InvokeMember($PropertyName, [Reflection.BindingFlags]::GetProperty, $null, $InputObject, $ArgumentList, $null, $null, $null)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTOperatingSystemInfo
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTOperatingSystemInfo
+{
+ <#
+ .SYNOPSIS
+ Gets information about the current computer's operating system.
+
+ .DESCRIPTION
+ Gets information about the current computer's operating system, such as name, version, edition, and other information.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.OperatingSystem.OSVersionInfo
+
+ Returns an PSADT.OperatingSystem.OSVersionInfo object containing the current computer's operating system information.
+
+ .EXAMPLE
+ Get-ADTOperatingSystemInfo
+
+ Gets an PSADT.OperatingSystem.OSVersionInfo object containing the current computer's operating system information.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTOperatingSystemInfo
+ #>
+
+ return [PSADT.OperatingSystem.OSVersionInfo]::Current
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTPEFileArchitecture
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTPEFileArchitecture
+{
+ <#
+ .SYNOPSIS
+ Determine if a PE file is a 32-bit or a 64-bit file.
+
+ .DESCRIPTION
+ Determine if a PE file is a 32-bit or a 64-bit file by examining the file's image file header.
+
+ PE file extensions: .exe, .dll, .ocx, .drv, .sys, .scr, .efi, .cpl, .fon
+
+ .PARAMETER FilePath
+ Path to the PE file to examine.
+
+ .PARAMETER PassThru
+ Get the file object, attach a property indicating the file binary type, and write to pipeline.
+
+ .INPUTS
+ System.IO.FileInfo
+
+ Accepts a FileInfo object from the pipeline.
+
+ .OUTPUTS
+ System.String
+
+ Returns a string indicating the file binary type.
+
+ .EXAMPLE
+ Get-ADTPEFileArchitecture -FilePath "$env:windir\notepad.exe"
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTPEFileArchitecture
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.IO.FileInfo])]
+ [OutputType([PSADT.Shared.SystemArchitecture])]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [ValidateScript({
+ if (!$_.Exists -or ($_ -notmatch '\.(exe|dll|ocx|drv|sys|scr|efi|cpl|fon)$'))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'One or more files either does not exist or has an invalid extension.'))
+ }
+ return !!$_
+ })]
+ [System.IO.FileInfo[]]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ [System.Int32]$MACHINE_OFFSET = 4
+ [System.Int32]$PE_POINTER_OFFSET = 60
+ [System.Byte[]]$data = [System.Byte[]]::new(4096)
+ }
+
+ process
+ {
+ foreach ($Path in $filePath)
+ {
+ try
+ {
+ try
+ {
+ # Read the first 4096 bytes of the file.
+ $stream = [System.IO.FileStream]::new($Path.FullName, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
+ $null = $stream.Read($data, 0, $data.Count)
+ $stream.Flush()
+ $stream.Close()
+
+ # Get the file header from the header's address, factoring in any offsets.
+ $peArchValue = [System.BitConverter]::ToUInt16($data, [System.BitConverter]::ToInt32($data, $PE_POINTER_OFFSET) + $MACHINE_OFFSET)
+ $peArchEnum = [PSADT.Shared.SystemArchitecture]::Unknown; $null = [PSADT.Shared.SystemArchitecture]::TryParse($peArchValue, [ref]$peArchEnum)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "File [$($Path.FullName)] has a detected file architecture of [$peArchEnum]."
+ if ($PassThru)
+ {
+ return ($Path | & $Script:CommandTable.'Add-Member' -MemberType NoteProperty -Name BinaryType -Value $peArchEnum -Force -PassThru)
+ }
+ return $peArchEnum
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTPendingReboot
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTPendingReboot
+{
+ <#
+ .SYNOPSIS
+ Get the pending reboot status on a local computer.
+
+ .DESCRIPTION
+ Check WMI and the registry to determine if the system has a pending reboot operation from any of the following:
+
+ - Component Based Servicing (Vista, Windows 2008)
+ - Windows Update / Auto Update (XP, Windows 2003 / 2008)
+ - SCCM 2012 Clients (DetermineIfRebootPending WMI method)
+ - App-V Pending Tasks (global based Appv 5.0 SP2)
+ - Pending File Rename Operations (XP, Windows 2003 / 2008)
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.RebootInfo
+
+ Returns a custom object with the following properties:
+
+ - ComputerName
+ - LastBootUpTime
+ - IsSystemRebootPending
+ - IsCBServicingRebootPending
+ - IsWindowsUpdateRebootPending
+ - IsSCCMClientRebootPending
+ - IsIntuneClientRebootPending
+ - IsFileRenameRebootPending
+ - PendingFileRenameOperations
+ - ErrorMsg
+
+ .EXAMPLE
+ Get-ADTPendingReboot
+
+ This example retrieves the pending reboot status on the local computer and returns a custom object with detailed information.
+
+ .EXAMPLE
+ (Get-ADTPendingReboot).IsSystemRebootPending
+
+ This example returns a boolean value determining whether or not there is a pending reboot operation.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ ErrorMsg only contains something if an error occurred.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTPendingReboot
+ #>
+
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.RebootInfo])]
+ param
+ (
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $PendRebootErrorMsg = [System.Collections.Specialized.StringCollection]::new()
+ $HostName = [System.Net.Dns]::GetHostName()
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Get the date/time that the system last booted up.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Getting the pending reboot status on the local computer [$HostName]."
+ $LastBootUpTime = [System.DateTime]::Now - [System.TimeSpan]::FromMilliseconds([System.Environment]::TickCount)
+
+ # Determine if a Windows Vista/Server 2008 and above machine has a pending reboot from a Component Based Servicing (CBS) operation.
+ $IsCBServicingRebootPending = & $Script:CommandTable.'Test-Path' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
+
+ # Determine if there is a pending reboot from a Windows Update.
+ $IsWindowsUpdateRebootPending = & $Script:CommandTable.'Test-Path' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired'
+
+ # Determine if there is a pending reboot from an App-V global Pending Task. (User profile based tasks will complete on logoff/logon).
+ $IsAppVRebootPending = & $Script:CommandTable.'Test-Path' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Software\Microsoft\AppV\Client\PendingTasks'
+
+ # Get the value of PendingFileRenameOperations.
+ $IsFileRenameRebootPending = !!($PendingFileRenameOperations = & $Script:CommandTable.'Get-ItemProperty' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager' | & $Script:CommandTable.'Select-Object' -ExpandProperty PendingFileRenameOperations -ErrorAction Ignore)
+
+ # Determine SCCM 2012 Client reboot pending status.
+ $IsSCCMClientRebootPending = if ((& $Script:CommandTable.'Get-CimInstance' -Namespace root -ClassName __NAMESPACE -Verbose:$false).Name.Contains('CCM'))
+ {
+ try
+ {
+ if (($SCCMClientRebootStatus = & $Script:CommandTable.'Invoke-CimMethod' -Namespace ROOT\CCM\ClientSDK -ClassName CCM_ClientUtilities -Name DetermineIfRebootPending -Verbose:$false).ReturnValue -ne 0)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("The 'DetermineIfRebootPending' method of 'ROOT\CCM\ClientSDK\CCM_ClientUtilities' class returned error code [$($SCCMClientRebootStatus.ReturnValue)].")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'DetermineIfRebootPendingInvalidReturn'
+ TargetObject = $SCCMClientRebootStatus
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ $SCCMClientRebootStatus.IsHardRebootPending -or $SCCMClientRebootStatus.RebootPending
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to get IsSCCMClientRebootPending.`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 3
+ $null = $PendRebootErrorMsg.Add("Failed to get IsSCCMClientRebootPending: $($_.Exception.Message)")
+ }
+ }
+
+ # Determine Intune Management Extension reboot pending status.
+ $IsIntuneClientRebootPending = & $Script:CommandTable.'Test-Path' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IntuneManagementExtension\RebootSettings\RebootFlag'
+
+ # Create a custom object containing pending reboot information for the system.
+ $PendingRebootInfo = [PSADT.Types.RebootInfo]::new(
+ $HostName,
+ $LastBootUpTime,
+ $IsCBServicingRebootPending -or $IsWindowsUpdateRebootPending -or $IsFileRenameRebootPending -or $IsSCCMClientRebootPending,
+ $IsCBServicingRebootPending,
+ $IsWindowsUpdateRebootPending,
+ $IsSCCMClientRebootPending,
+ $IsIntuneClientRebootPending,
+ $IsAppVRebootPending,
+ $IsFileRenameRebootPending,
+ $PendingFileRenameOperations,
+ $PendRebootErrorMsg
+ )
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Pending reboot status on the local computer [$HostName]:`n$($PendingRebootInfo | & $Script:CommandTable.'Format-List' | & $Script:CommandTable.'Out-String' -Width ([System.Int32]::MaxValue))"
+ return $PendingRebootInfo
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTPowerShellProcessPath
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTPowerShellProcessPath
+{
+ <#
+ .SYNOPSIS
+ Retrieves the path to the PowerShell executable.
+
+ .DESCRIPTION
+ The Get-ADTPowerShellProcessPath function returns the path to the PowerShell executable. It determines whether the current PowerShell session is running in Windows PowerShell or PowerShell Core and returns the appropriate executable path.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the path to the PowerShell executable as a string.
+
+ .EXAMPLE
+ Get-ADTPowerShellProcessPath
+
+ This example retrieves the path to the PowerShell executable for the current session.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTPowerShellProcessPath
+ #>
+
+ return "$PSHOME\$(('powershell.exe', 'pwsh.exe')[$PSVersionTable.PSEdition.Equals('Core')])"
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTPresentationSettingsEnabledUsers
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTPresentationSettingsEnabledUsers
+{
+ <#
+ .SYNOPSIS
+ Tests whether any users have presentation mode enabled on their device.
+
+ .DESCRIPTION
+ Tests whether any users have presentation mode enabled on their device. This can be enabled via the PC's Mobility Settings, or with PresentationSettings.exe.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.UserProfile
+
+ Returns one or more UserProfile objects of the users with presentation mode enabled on their device.
+
+ .EXAMPLE
+ Get-ADTPresentationSettingsEnabledUsers
+
+ Checks whether any users users have presentation settings enabled on their device and returns an associated UserProfile object.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTPresentationSettingsEnabledUsers
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.UserProfile])]
+ param
+ (
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Checking whether any logged on users are in presentation mode..."
+ try
+ {
+ try
+ {
+ # Build out params for Invoke-ADTAllUsersRegistryAction.
+ $iaauraParams = @{
+ ScriptBlock = { if (& $Script:CommandTable.'Get-ADTRegistryKey' -Key Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\MobilePC\AdaptableSettings\Activity -Name Activity -SID $_.SID) { return $_ } }
+ UserProfiles = & $Script:CommandTable.'Get-ADTUserProfiles' -ExcludeDefaultUser -InformationAction SilentlyContinue
+ }
+
+ # Return UserProfile objects for each user with "I am currently giving a presentation" enabled.
+ if (($usersInPresentationMode = & $Script:CommandTable.'Invoke-ADTAllUsersRegistryAction' @iaauraParams -SkipUnloadedProfiles -InformationAction SilentlyContinue))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The following users are currently in presentation mode: ['$([System.String]::Join("', '", $usersInPresentationMode.NTAccount))']."
+ return $usersInPresentationMode
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "There are no logged on users in presentation mode."
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTRegistryKey
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTRegistryKey
+{
+ <#
+ .SYNOPSIS
+ Retrieves value names and value data for a specified registry key or optionally, a specific value.
+
+ .DESCRIPTION
+ Retrieves value names and value data for a specified registry key or optionally, a specific value. If the registry key does not exist or contain any values, the function will return $null by default.
+
+ To test for existence of a registry key path, use built-in Test-Path cmdlet.
+
+ .PARAMETER Key
+ Path of the registry key.
+
+ .PARAMETER Name
+ Value name to retrieve (optional).
+
+ .PARAMETER Wow6432Node
+ Specify this switch to read the 32-bit registry (Wow6432Node) on 64-bit systems.
+
+ .PARAMETER SID
+ The security identifier (SID) for a user. Specifying this parameter will convert a HKEY_CURRENT_USER registry key to the HKEY_USERS\$SID format.
+
+ Specify this parameter from the Invoke-ADTAllUsersRegistryAction function to read/edit HKCU registry settings for all users on the system.
+
+ .PARAMETER ReturnEmptyKeyIfExists
+ Return the registry key if it exists but it has no property/value pairs underneath it.
+
+ .PARAMETER DoNotExpandEnvironmentNames
+ Return unexpanded REG_EXPAND_SZ values.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the value of the registry key or value.
+
+ .EXAMPLE
+ Get-ADTRegistryKey -Key 'HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1AD147D0-BE0E-3D6C-AC11-64F6DC4163F1}'
+
+ This example retrieves all value names and data for the specified registry key.
+
+ .EXAMPLE
+ Get-ADTRegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\iexplore.exe'
+
+ This example retrieves all value names and data for the specified registry key.
+
+ .EXAMPLE
+ Get-ADTRegistryKey -Key 'HKLM:Software\Wow6432Node\Microsoft\Microsoft SQL Server Compact Edition\v3.5' -Name 'Version'
+
+ This example retrieves the 'Version' value data for the specified registry key.
+
+ .EXAMPLE
+ Get-ADTRegistryKey -Key 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' -Name 'Path' -DoNotExpandEnvironmentNames
+
+ This example retrieves the 'Path' value data without expanding environment variables.
+
+ .EXAMPLE
+ Get-ADTRegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Example' -Name '(Default)'
+
+ This example retrieves the default value data for the specified registry key.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTRegistryKey
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ReturnEmptyKeyIfExists,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DoNotExpandEnvironmentNames
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If the SID variable is specified, then convert all HKEY_CURRENT_USER key's to HKEY_USERS\$SID.
+ $Key = if ($PSBoundParameters.ContainsKey('SID'))
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key -Wow6432Node:$Wow6432Node -SID $SID
+ }
+ else
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key -Wow6432Node:$Wow6432Node
+ }
+
+ # Check if the registry key exists before continuing.
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $Key))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Registry key [$Key] does not exist. Return `$null." -Severity 2
+ return
+ }
+
+ if ($PSBoundParameters.ContainsKey('Name'))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Getting registry key [$Key] value [$Name]."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Getting registry key [$Key] and all property values."
+ }
+
+ # Get all property values for registry key.
+ $regKeyValue = & $Script:CommandTable.'Get-ItemProperty' -LiteralPath $Key
+ $regKeyValuePropertyCount = $regKeyValue | & $Script:CommandTable.'Measure-Object' | & $Script:CommandTable.'Select-Object' -ExpandProperty Count
+
+ # Select requested property.
+ if ($PSBoundParameters.ContainsKey('Name'))
+ {
+ # Get the Value (do not make a strongly typed variable because it depends entirely on what kind of value is being read)
+ if ((& $Script:CommandTable.'Get-Item' -LiteralPath $Key | & $Script:CommandTable.'Select-Object' -ExpandProperty Property -ErrorAction Ignore) -notcontains $Name)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Registry key value [$Key] [$Name] does not exist. Return `$null."
+ return
+ }
+ if ($DoNotExpandEnvironmentNames)
+ {
+ # Only useful on 'ExpandString' values.
+ if ($Name -like '(Default)')
+ {
+ return (& $Script:CommandTable.'Get-Item' -LiteralPath $Key).GetValue($null, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
+ }
+ else
+ {
+ return (& $Script:CommandTable.'Get-Item' -LiteralPath $Key).GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
+ }
+ }
+ elseif ($Name -like '(Default)')
+ {
+ return (& $Script:CommandTable.'Get-Item' -LiteralPath $Key).GetValue($null)
+ }
+ else
+ {
+ return $regKeyValue | & $Script:CommandTable.'Select-Object' -ExpandProperty $Name
+ }
+ }
+ elseif ($regKeyValuePropertyCount -eq 0)
+ {
+ # Select all properties or return empty key object.
+ if ($ReturnEmptyKeyIfExists)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "No property values found for registry key. Return empty registry key object [$Key]."
+ return (& $Script:CommandTable.'Get-Item' -LiteralPath $Key -Force)
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "No property values found for registry key. Return `$null."
+ return
+ }
+ }
+
+ # Return the populated registry key to the caller.
+ return $regKeyValue
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to read registry key [$Key]$(if ($Name) {" value [$Name]"})."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTRunAsActiveUser
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTRunAsActiveUser
+{
+ <#
+ .SYNOPSIS
+ Retrieves the active user session information.
+
+ .DESCRIPTION
+ The Get-ADTRunAsActiveUser function determines the account that will be used to execute commands in the user session when the toolkit is running under the SYSTEM account.
+
+ The active console user will be chosen first. If no active console user is found, for multi-session operating systems, the first logged-on user will be used instead.
+
+ .PARAMETER UserSessionInfo
+ An array of UserSessionInfo objects to enumerate through. If not supplied, a fresh query will be performed.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.UserSessionInfo
+
+ Returns a custom object containing the user session information.
+
+ .EXAMPLE
+ Get-ADTRunAsActiveUser
+
+ This example retrieves the active user session information.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTRunAsActiveUser
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.WTSSession.CompatibilitySessionInfo[]]$UserSessionInfo = (& $Script:CommandTable.'Get-ADTLoggedOnUser')
+ )
+
+ # Determine the account that will be used to execute commands in the user session when toolkit is running under the SYSTEM account.
+ # The active console user will be chosen first. Failing that, for multi-session operating systems, the first logged on user will be used instead.
+ try
+ {
+ $sessionInfoMember = if (& $Script:CommandTable.'Test-ADTIsMultiSessionOS') { 'IsCurrentSession' } else { 'IsActiveUserSession' }
+ foreach ($userSessionInfo in $UserSessionInfo)
+ {
+ if ($userSessionInfo.NTAccount -and $userSessionInfo.$sessionInfoMember)
+ {
+ return $userSessionInfo
+ }
+ }
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTSchedulerTask
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTSchedulerTask
+{
+ <#
+ .SYNOPSIS
+ Retrieve all details for scheduled tasks on the local computer.
+
+ .DESCRIPTION
+ Retrieve all details for scheduled tasks on the local computer using schtasks.exe. All property names have spaces and colons removed.
+
+ This function is deprecated. Please migrate your scripts to use the built-in Get-ScheduledTask Cmdlet.
+
+ .PARAMETER TaskName
+ Specify the name of the scheduled task to retrieve details for. Uses regex match to find scheduled task.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.PSObject
+
+ This function returns a PSObject with all scheduled task properties.
+
+ .EXAMPLE
+ Get-ADTSchedulerTask
+
+ This example retrieves a list of all scheduled task properties.
+
+ .EXAMPLE
+ Get-ADTSchedulerTask | Out-GridView
+
+ This example displays a grid view of all scheduled task properties.
+
+ .EXAMPLE
+ Get-ADTSchedulerTask | Select-Object -Property TaskName
+
+ This example displays a list of all scheduled task names.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTSchedulerTask
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'TaskName', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TaskName
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Advise that this function is considered deprecated.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The function [$($MyInvocation.MyCommand.Name)] is deprecated. Please migrate your scripts to use the built-in [Get-ScheduledTask] Cmdlet." -Severity 2
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Retrieving Scheduled Tasks...'
+ try
+ {
+ try
+ {
+ # Get CSV data from the binary and confirm success.
+ $exeSchtasksResults = & "$([System.Environment]::SystemDirectory)\schtasks.exe" /Query /V /FO CSV 2>&1
+ if ($Global:LASTEXITCODE -ne 0)
+ {
+ $naerParams = @{
+ Exception = [System.Runtime.InteropServices.ExternalException]::new("The call to [$([System.Environment]::SystemDirectory)\schtasks.exe] failed with exit code [$Global:LASTEXITCODE].", $Global:LASTEXITCODE)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'SchTasksExecutableFailure'
+ TargetObject = $exeSchtasksResults
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Convert CSV data to objects and re-process to remove non-word characters before returning data to the caller.
+ if (($schTasks = $exeSchtasksResults | & $Script:CommandTable.'ConvertFrom-Csv' | & { process { if (($_.TaskName -match '^\\') -and ([string]::IsNullOrWhiteSpace($TaskName) -or $_.TaskName -match $TaskName)) { return $_ } } }))
+ {
+ return $schTasks | & $Script:CommandTable.'Select-Object' -Property ($schTasks[0].PSObject.Properties.Name | & {
+ process
+ {
+ @{ Label = $_ -replace '[^\w]'; Expression = [scriptblock]::Create("`$_.'$_'") }
+ }
+ })
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to retrieve scheduled tasks."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTServiceStartMode
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTServiceStartMode
+{
+ <#
+ .SYNOPSIS
+ Retrieves the startup mode of a specified service.
+
+ .DESCRIPTION
+ Retrieves the startup mode of a specified service. This function checks the service's start type and adjusts the result if the service is set to 'Automatic (Delayed Start)'.
+
+ .PARAMETER Service
+ Specify the service object to retrieve the startup mode for.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the startup mode of the specified service.
+
+ .EXAMPLE
+ Get-ADTServiceStartMode -Service (Get-Service -Name 'wuauserv')
+
+ Retrieves the startup mode of the 'wuauserv' service.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTServiceStartMode
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (!$_.Name)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Service -ProvidedValue $_ -ExceptionMessage 'The specified service does not exist.'))
+ }
+ return !!$_
+ })]
+ [System.ServiceProcess.ServiceController]$Service
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Getting the service [$($Service.Name)] startup mode."
+ try
+ {
+ try
+ {
+ # Get the start mode and adjust it if the automatic type is delayed.
+ if ((($serviceStartMode = $Service.StartType) -eq 'Automatic') -and ((& $Script:CommandTable.'Get-ItemProperty' -LiteralPath "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$($Service.Name)" -ErrorAction Ignore | & $Script:CommandTable.'Select-Object' -ExpandProperty DelayedAutoStart -ErrorAction Ignore) -eq 1))
+ {
+ $serviceStartMode = 'Automatic (Delayed Start)'
+ }
+
+ # Return startup type to the caller.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Service [$($Service.Name)] startup mode is set to [$serviceStartMode]."
+ return $serviceStartMode
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTSession
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTSession
+{
+ <#
+ .SYNOPSIS
+ Retrieves the most recent ADT session.
+
+ .DESCRIPTION
+ The Get-ADTSession function returns the most recent session from the ADT module data. If no sessions are found, it throws an error indicating that an ADT session should be opened using Open-ADTSession before calling this function.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ ADTSession
+
+ Returns the most recent session object from the ADT module data.
+
+ .EXAMPLE
+ Get-ADTSession
+
+ This example retrieves the most recent ADT session.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Requires: PSADT session should be initialized using Open-ADTSession
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTSession
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ # Return the most recent session in the database.
+ if (!$Script:ADT.Sessions.Count)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Please ensure that [Open-ADTSession] is called before using any $($MyInvocation.MyCommand.Module.Name) functions.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'ADTSessionBufferEmpty'
+ TargetObject = $Script:ADT.Sessions
+ RecommendedAction = "Please ensure a session is opened via [Open-ADTSession] and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ return $Script:ADT.Sessions[-1]
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTShortcut
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTShortcut
+{
+ <#
+ .SYNOPSIS
+ Get information from a .lnk or .url type shortcut.
+
+ .DESCRIPTION
+ Get information from a .lnk or .url type shortcut. Returns a hashtable with details about the shortcut such as TargetPath, Arguments, Description, and more.
+
+ .PARAMETER Path
+ Path to the shortcut to get information from.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Collections.Hashtable
+
+ Returns a hashtable with the following keys:
+ - TargetPath
+ - Arguments
+ - Description
+ - WorkingDirectory
+ - WindowStyle
+ - Hotkey
+ - IconLocation
+ - IconIndex
+ - RunAsAdmin
+
+ .EXAMPLE
+ Get-ADTShortcut -Path "$envProgramData\Microsoft\Windows\Start Menu\My Shortcut.lnk"
+
+ Retrieves information from the specified .lnk shortcut.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Url shortcuts only support TargetPath, IconLocation, and IconIndex.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTShortcut
+ #>
+
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.ShortcutUrl])]
+ [OutputType([PSADT.Types.ShortcutLnk])]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_) -or (![System.IO.Path]::GetExtension($_).ToLower().Equals('.lnk') -and ![System.IO.Path]::GetExtension($_).ToLower().Equals('.url')))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified path does not exist or does not have the correct extension.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$Path
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ # Make sure .NET's current directory is synced with PowerShell's.
+ try
+ {
+ try
+ {
+ [System.IO.Directory]::SetCurrentDirectory((& $Script:CommandTable.'Get-Location' -PSProvider FileSystem).ProviderPath)
+ $Output = @{ Path = [System.IO.Path]::GetFullPath($Path); TargetPath = $null; IconIndex = $null; IconLocation = $null }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Specified path [$Path] is not valid."
+ return
+ }
+
+ try
+ {
+ try
+ {
+ # Build out remainder of object.
+ if ([System.IO.Path]::GetExtension($Output.Path) -eq '.url')
+ {
+ [System.IO.File]::ReadAllLines($Output.Path) | & {
+ process
+ {
+ switch ($_)
+ {
+ { $_.StartsWith('URL=') } { $Output.TargetPath = $_.Replace('URL=', $null); break }
+ { $_.StartsWith('IconIndex=') } { $Output.IconIndex = $_.Replace('IconIndex=', $null); break }
+ { $_.StartsWith('IconFile=') } { $Output.IconLocation = $_.Replace('IconFile=', $null); break }
+ }
+ }
+ }
+ return [PSADT.Types.ShortcutUrl]::new(
+ $Output.Path,
+ $Output.TargetPath,
+ $Output.IconIndex,
+ $Output.IconLocation
+ )
+ }
+ else
+ {
+ $shortcut = [System.Activator]::CreateInstance([System.Type]::GetTypeFromProgID('WScript.Shell')).CreateShortcut($Output.Path)
+ $Output.IconLocation, $Output.IconIndex = $shortcut.IconLocation.Split(',')
+ return [PSADT.Types.ShortcutLnk]::new(
+ $Output.Path,
+ $shortcut.TargetPath,
+ $Output.IconIndex,
+ $Output.IconLocation,
+ $shortcut.Arguments,
+ $shortcut.Description,
+ $shortcut.WorkingDirectory,
+ $(switch ($shortcut.WindowStyle)
+ {
+ 1 { 'Normal'; break }
+ 3 { 'Maximized'; break }
+ 7 { 'Minimized'; break }
+ default { 'Normal'; break }
+ }),
+ $shortcut.Hotkey,
+ !!([System.IO.File]::ReadAllBytes($Output.Path)[21] -band 32)
+ )
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to read the shortcut [$($Output.Path)]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTStringTable
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTStringTable
+{
+ <#
+ .SYNOPSIS
+ Retrieves the string database from the ADT module.
+
+ .DESCRIPTION
+ The Get-ADTStringTable function returns the string database if it has been initialized. If the string database is not initialized, it throws an error indicating that Initialize-ADTModule should be called before using this function.
+
+ .INPUTS
+ None
+
+ This function does not take any pipeline input.
+
+ .OUTPUTS
+ System.Collections.Hashtable
+
+ Returns a hashtable containing the string database.
+
+ .EXAMPLE
+ Get-ADTStringTable
+
+ This example retrieves the string database from the ADT module.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Requires: The module should be initialized using Initialize-ADTModule
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTStringTable
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ # Return the string database if initialized.
+ if (!$Script:ADT.Strings -or !$Script:ADT.Strings.Count)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Please ensure that [Initialize-ADTModule] is called before using any $($MyInvocation.MyCommand.Module.Name) functions.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'ADTStringTableNotInitialized'
+ TargetObject = $Script:ADT.Strings
+ RecommendedAction = "Please ensure the module is initialized via [Initialize-ADTModule] and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ return $Script:ADT.Strings
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTUniversalDate
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTUniversalDate
+{
+ <#
+ .SYNOPSIS
+ Returns the date/time for the local culture in a universal sortable date time pattern.
+
+ .DESCRIPTION
+ Converts the current datetime or a datetime string for the current culture into a universal sortable date time pattern, e.g. 2013-08-22 11:51:52Z.
+
+ .PARAMETER DateTime
+ Specify the DateTime in the current culture.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the date/time for the local culture in a universal sortable date time pattern.
+
+ .EXAMPLE
+ Get-ADTUniversalDate
+
+ Returns the current date in a universal sortable date time pattern.
+
+ .EXAMPLE
+ Get-ADTUniversalDate -DateTime '25/08/2013'
+
+ Returns the date for the current culture in a universal sortable date time pattern.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTUniversalDate
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DateTime = [System.DateTime]::Now.ToString([System.Globalization.DateTimeFormatInfo]::CurrentInfo.UniversalSortableDateTimePattern).TrimEnd('Z')
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Convert the date to a universal sortable date time pattern based on the current culture.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Converting the date [$DateTime] to a universal sortable date time pattern based on the current culture [$($Host.CurrentCulture.Name)]."
+ return [System.DateTime]::Parse($DateTime, $Host.CurrentCulture).ToUniversalTime().ToString([System.Globalization.DateTimeFormatInfo]::CurrentInfo.UniversalSortableDateTimePattern)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "The specified date/time [$DateTime] is not in a format recognized by the current culture [$($Host.CurrentCulture.Name)]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTUserProfiles
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTUserProfiles
+{
+ <#
+ .SYNOPSIS
+ Get the User Profile Path, User Account SID, and the User Account Name for all users that log onto the machine and also the Default User.
+
+ .DESCRIPTION
+ Get the User Profile Path, User Account SID, and the User Account Name for all users that log onto the machine and also the Default User (which does not log on).
+
+ Please note that the NTAccount property may be empty for some user profiles but the SID and ProfilePath properties will always be populated.
+
+ .PARAMETER ExcludeNTAccount
+ Specify NT account names in DOMAIN\username format to exclude from the list of user profiles.
+
+ .PARAMETER IncludeSystemProfiles
+ Include system profiles: SYSTEM, LOCAL SERVICE, NETWORK SERVICE.
+
+ .PARAMETER IncludeServiceProfiles
+ Include service (NT SERVICE) profiles.
+
+ .PARAMETER IncludeIISAppPoolProfiles
+ Include IIS AppPool profiles. Excluded by default as they don't parse well.
+
+ .PARAMETER ExcludeDefaultUser
+ Exclude the Default User.
+
+ .PARAMETER LoadProfilePaths
+ Load additional profile paths for each user profile.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.UserProfile
+
+ Returns a PSADT.Types.UserProfile object with the following properties:
+ - NTAccount
+ - SID
+ - ProfilePath
+
+ .EXAMPLE
+ Get-ADTUserProfiles
+
+ Return the following properties for each user profile on the system: NTAccount, SID, ProfilePath.
+
+ .EXAMPLE
+ Get-ADTUserProfiles -ExcludeNTAccount CONTOSO\Robot,CONTOSO\ntadmin
+
+ Return the following properties for each user profile on the system, except for 'Robot' and 'ntadmin': NTAccount, SID, ProfilePath.
+
+ .EXAMPLE
+ [string[]]$ProfilePaths = Get-ADTUserProfiles | Select-Object -ExpandProperty ProfilePath
+
+ Return the user profile path for each user on the system. This information can then be used to make modifications under the user profile on the filesystem.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTUserProfiles
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ExcludeNTAccount', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.UserProfile])]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.Principal.NTAccount[]]$ExcludeNTAccount,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeSystemProfiles,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeServiceProfiles,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeIISAppPoolProfiles,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeDefaultUser,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$LoadProfilePaths
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $userProfileListRegKey = 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
+ $excludedSids = "^S-1-5-($([System.String]::Join('|', $(
+ if (!$IncludeSystemProfiles)
+ {
+ 18 # System (or LocalSystem)
+ 19 # NT Authority (LocalService)
+ 20 # Network Service
+ }
+ if (!$IncludeServiceProfiles)
+ {
+ 80 # NT Service
+ }
+ if (!$IncludeIISAppPoolProfiles)
+ {
+ 82 # IIS AppPool
+ }
+ ))))"
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Getting the User Profile Path, User Account SID, and the User Account Name for all users that log onto the machine.'
+ try
+ {
+ try
+ {
+ # Get the User Profile Path, User Account SID, and the User Account Name for all users that log onto the machine.
+ & $Script:CommandTable.'Get-ItemProperty' -Path "$userProfileListRegKey\*" | & {
+ process
+ {
+ # Return early if the SID is to be excluded.
+ if ($_.PSChildName -match $excludedSids)
+ {
+ return
+ }
+
+ # Return early for accounts that have a null NTAccount.
+ if (!($ntAccount = & $Script:CommandTable.'ConvertTo-ADTNTAccountOrSID' -SID $_.PSChildName))
+ {
+ return
+ }
+
+ # Return early for excluded accounts.
+ if ($ExcludeNTAccount -contains $ntAccount)
+ {
+ return
+ }
+
+ # Establish base profile.
+ $userProfile = [PSADT.Types.UserProfile]::new(
+ $ntAccount,
+ $_.PSChildName,
+ $_.ProfileImagePath
+ )
+
+ # Append additional info if requested.
+ if ($LoadProfilePaths)
+ {
+ $userProfile = & $Script:CommandTable.'Invoke-ADTAllUsersRegistryAction' -UserProfiles $userProfile -ScriptBlock {
+ [PSADT.Types.UserProfile]::new(
+ $_.NTAccount,
+ $_.SID,
+ $_.ProfilePath,
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'AppData' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Local AppData' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Desktop' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Personal' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Start Menu' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Environment' -Name 'TEMP' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Environment' -Name 'OneDrive' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Environment' -Name 'OneDriveCommercial' -SID $_.SID -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $_.ProfilePath)
+ )
+ }
+ }
+
+ # Write out the object to the pipeline.
+ return $userProfile
+ }
+ }
+
+ # Create a custom object for the Default User profile. Since the Default User is not an actual user account, it does not have a username or a SID.
+ # We will make up a SID and add it to the custom object so that we have a location to load the default registry hive into later on.
+ if (!$ExcludeDefaultUser)
+ {
+ # The path to the default profile is stored in the default string value for the key.
+ $defaultUserProfilePath = (& $Script:CommandTable.'Get-ItemProperty' -LiteralPath $userProfileListRegKey).Default
+
+ # Retrieve additional information if requested.
+ if ($LoadProfilePaths)
+ {
+ return [PSADT.Types.UserProfile]::new(
+ 'Default',
+ [System.Security.Principal.SecurityIdentifier]::new([System.Security.Principal.WellKnownSidType]::NullSid, $null),
+ $defaultUserProfilePath,
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'AppData' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Local AppData' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Desktop' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Personal' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders' -Name 'Start Menu' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Environment' -Name 'TEMP' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Environment' -Name 'OneDrive' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath),
+ $((& $Script:CommandTable.'Get-ADTRegistryKey' -Key 'Microsoft.PowerShell.Core\Registry::HKEY_USERS\.DEFAULT\Environment' -Name 'OneDriveCommercial' -DoNotExpandEnvironmentNames) -replace '%USERPROFILE%', $defaultUserProfilePath)
+ )
+ }
+ return [PSADT.Types.UserProfile]::new(
+ 'Default',
+ 'S-1-0-0',
+ $defaultUserProfilePath
+ )
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Get-ADTWindowTitle
+#
+#-----------------------------------------------------------------------------
+
+function Get-ADTWindowTitle
+{
+ <#
+ .SYNOPSIS
+ Search for an open window title and return details about the window.
+
+ .DESCRIPTION
+ Search for a window title. If window title searched for returns more than one result, then details for each window will be displayed.
+
+ Returns the following properties for each window:
+
+ - WindowTitle
+ - WindowHandle
+ - ParentProcess
+ - ParentProcessMainWindowHandle
+ - ParentProcessId
+
+ Function does not work in SYSTEM context unless launched with "psexec.exe -s -i" to run it as an interactive process under the SYSTEM account.
+
+ .PARAMETER WindowTitle
+ One or more titles of the application window to search for using regex matching.
+
+ .PARAMETER WindowHandle
+ One or more window handles of the application window to search for.
+
+ .PARAMETER ParentProcess
+ One or more process names of the application window to search for.
+
+ .PARAMETER GetAllWindowTitles
+ Get titles for all open windows on the system.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.WindowInfo
+
+ Returns a PSADT.Types.WindowInfo object with the following properties:
+
+ - WindowTitle
+ - WindowHandle
+ - ParentProcess
+ - ParentProcessMainWindowHandle
+ - ParentProcessId
+
+ .EXAMPLE
+ Get-ADTWindowTitle -WindowTitle 'Microsoft Word'
+
+ Gets details for each window that has the words "Microsoft Word" in the title.
+
+ .EXAMPLE
+ Get-ADTWindowTitle -GetAllWindowTitles
+
+ Gets details for all windows with a title.
+
+ .EXAMPLE
+ Get-ADTWindowTitle -GetAllWindowTitles | Where-Object { $_.ParentProcess -eq 'WINWORD' }
+
+ Get details for all windows belonging to Microsoft Word process with name "WINWORD".
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Function does not work in SYSTEM context unless launched with "psexec.exe -s -i" to run it as an interactive process under the SYSTEM account.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Get-ADTWindowTitle
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'WindowTitle', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'WindowHandle', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ParentProcess', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'GetAllWindowTitles', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.WindowInfo])]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'SearchWinTitle')]
+ [AllowEmptyString()]
+ [System.String[]]$WindowTitle,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SearchWinHandle')]
+ [AllowEmptyString()]
+ [System.IntPtr[]]$WindowHandle,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SearchParentProcess')]
+ [AllowEmptyString()]
+ [System.String[]]$ParentProcess,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'GetAllWinTitles')]
+ [System.Management.Automation.SwitchParameter]$GetAllWindowTitles
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ # Announce commencement.
+ switch ($PSCmdlet.ParameterSetName)
+ {
+ GetAllWinTitles
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Finding all open window title(s).'
+ break
+ }
+ SearchWinTitle
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Finding open windows matching the specified title(s)."
+ break
+ }
+ SearchWinHandle
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Finding open windows matching the specified handle(s)."
+ break
+ }
+ SearchWinHandle
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Finding open windows matching the specified parent process(es)."
+ break
+ }
+ }
+
+ try
+ {
+ try
+ {
+ # Cache all running processes.
+ $processes = [System.Diagnostics.Process]::GetProcesses() | & {
+ process
+ {
+ if ($WindowHandle -and ($_.MainWindowHandle -notin $WindowHandle))
+ {
+ return
+ }
+ if ($ParentProcess -and ($_.ProcessName -notin $ParentProcess))
+ {
+ return
+ }
+ return $_
+ }
+ }
+
+ # Get all window handles for visible windows and loop through the visible ones.
+ [PSADT.GUI.UiAutomation]::EnumWindows() | & {
+ process
+ {
+ # Return early if we're null.
+ if ($null -eq $_)
+ {
+ return
+ }
+
+ # Return early if window isn't visible.
+ if (![PSADT.LibraryInterfaces.User32]::IsWindowVisible($_))
+ {
+ return
+ }
+
+ # Return early if the window doesn't have any text.
+ if (!($VisibleWindowTitle = [PSADT.GUI.UiAutomation]::GetWindowText($_)))
+ {
+ return
+ }
+
+ # Return early if the visible window title doesn't match our filter.
+ if ($WindowTitle -and ($VisibleWindowTitle -notmatch "($([System.String]::Join('|', $WindowTitle)))"))
+ {
+ return
+ }
+
+ # Return early if the window doesn't have an associated process.
+ if (!($process = $processes | & $Script:CommandTable.'Where-Object' -Property Id -EQ -Value ([PSADT.GUI.UiAutomation]::GetWindowThreadProcessId($_)) | & $Script:CommandTable.'Select-Object' -First 1))
+ {
+ return
+ }
+
+ # Build custom object with details about the window and the process.
+ return [PSADT.Types.WindowInfo]::new(
+ $VisibleWindowTitle,
+ $_,
+ $Process.ProcessName,
+ $Process.MainWindowHandle,
+ $Process.Id
+ )
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to get requested window title(s)."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Initialize-ADTFunction
+#
+#-----------------------------------------------------------------------------
+
+function Initialize-ADTFunction
+{
+ <#
+ .SYNOPSIS
+ Initializes the ADT function environment.
+
+ .DESCRIPTION
+ Initializes the ADT function environment by setting up necessary variables and logging function start details. It ensures that the function always stops on errors and handles verbose logging.
+
+ .PARAMETER Cmdlet
+ The cmdlet that is being initialized.
+
+ .PARAMETER SessionState
+ The session state of the cmdlet.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Initialize-ADTFunction -Cmdlet $PSCmdlet
+
+ Initializes the ADT function environment for the given cmdlet.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Initialize-ADTFunction
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.PSCmdlet]$Cmdlet,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SessionState]$SessionState
+ )
+
+ # Internal worker function to set variables within the caller's scope.
+ function Set-CallerVariable
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'This is an internal worker function that requires no end user confirmation.')]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$Value
+ )
+
+ # Directly go up the scope tree if its an in-session function.
+ if ($SessionState.Equals($ExecutionContext.SessionState))
+ {
+ & $Script:CommandTable.'Set-Variable' -Name $Name -Value $Value -Scope 2 -Force -Confirm:$false -WhatIf:$false
+ }
+ else
+ {
+ $SessionState.PSVariable.Set($Name, $Value)
+ }
+ }
+
+ # Ensure this function always stops, no matter what.
+ $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
+
+ # Write debug log messages.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Function Start' -Source $Cmdlet.MyInvocation.MyCommand.Name -DebugMessage
+ if ($Cmdlet.MyInvocation.BoundParameters.Count)
+ {
+ $CmdletBoundParameters = $Cmdlet.MyInvocation.BoundParameters | & $Script:CommandTable.'Format-Table' -Property @{ Label = 'Parameter'; Expression = { "[-$($_.Key)]" } }, @{ Label = 'Value'; Expression = { $_.Value }; Alignment = 'Left' }, @{ Label = 'Type'; Expression = { if ($_.Value) { $_.Value.GetType().Name } }; Alignment = 'Left' } -AutoSize -Wrap | & $Script:CommandTable.'Out-String' -Width ([System.Int32]::MaxValue)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Function invoked with bound parameter(s):`n$CmdletBoundParameters" -Source $Cmdlet.MyInvocation.MyCommand.Name -DebugMessage
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Function invoked without any bound parameters.' -Source $Cmdlet.MyInvocation.MyCommand.Name -DebugMessage
+ }
+
+ # Amend the caller's $ErrorActionPreference to archive off their provided value so we can always stop on a dime.
+ # For the caller-provided values, we deliberately use a string value to escape issues when 'Ignore' is passed.
+ # https://github.com/PowerShell/PowerShell/issues/1759#issuecomment-442916350
+ if ($Cmdlet.MyInvocation.BoundParameters.ContainsKey('ErrorAction'))
+ {
+ # Caller's value directly against the function.
+ Set-CallerVariable -Name OriginalErrorAction -Value $Cmdlet.MyInvocation.BoundParameters.ErrorAction.ToString()
+ }
+ elseif ($PSBoundParameters.ContainsKey('ErrorAction'))
+ {
+ # A function's own specified override.
+ Set-CallerVariable -Name OriginalErrorAction -Value $PSBoundParameters.ErrorAction.ToString()
+ }
+ else
+ {
+ # The module's default ErrorActionPreference.
+ Set-CallerVariable -Name OriginalErrorAction -Value $Script:ErrorActionPreference
+ }
+ Set-CallerVariable -Name ErrorActionPreference -Value $Script:ErrorActionPreference
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Initialize-ADTModule
+#
+#-----------------------------------------------------------------------------
+
+function Initialize-ADTModule
+{
+ <#
+ .SYNOPSIS
+ Initializes the ADT module by setting up necessary configurations and environment.
+
+ .DESCRIPTION
+ The Initialize-ADTModule function sets up the environment for the ADT module by initializing necessary variables, configurations, and string tables. It ensures that the module is not initialized while there is an active ADT session in progress. This function prepares the module for use by clearing callbacks, sessions, and setting up the environment table.
+
+ .PARAMETER ScriptDirectory
+ An override directory to use for config and string loading.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Initialize-ADTModule
+
+ Initializes the ADT module with the default settings and configurations.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Initialize-ADTModule
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ScriptDirectory -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (![System.IO.Directory]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ScriptDirectory -ProvidedValue $_ -ExceptionMessage 'The specified directory does not exist.'))
+ }
+ return $_
+ })]
+ [System.String[]]$ScriptDirectory
+ )
+
+ begin
+ {
+ # Log our start time to clock the module init duration.
+ $moduleInitStart = [System.DateTime]::Now
+
+ # Ensure this function isn't being called mid-flight.
+ if (& $Script:CommandTable.'Test-ADTSessionActive')
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("This function cannot be called while there is an active ADTSession in progress.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'InitWithActiveSessionError'
+ TargetObject = & $Script:CommandTable.'Get-ADTSession'
+ RecommendedAction = "Please attempt module re-initialization once the active ADTSession(s) have been closed."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Specify the base directory used when searching for config and string tables.
+ $Script:ADT.Directories.Script = if ($PSBoundParameters.ContainsKey('ScriptDirectory'))
+ {
+ $ScriptDirectory
+ }
+ else
+ {
+ $Script:ADT.Directories.Defaults.Script
+ }
+
+ # Initialize remaining directory paths.
+ 'Config', 'Strings' | & {
+ process
+ {
+ [System.String[]]$Script:ADT.Directories.$_ = foreach ($directory in $Script:ADT.Directories.Script)
+ {
+ if ([System.IO.File]::Exists([System.IO.Path]::Combine($directory, $_, "$($_.ToLower()).psd1")))
+ {
+ [System.IO.Path]::Combine($directory, $_)
+ }
+ }
+ if ($null -eq $Script:ADT.Directories.$_)
+ {
+ [System.String[]]$Script:ADT.Directories.$_ = $Script:ADT.Directories.Defaults.$_
+ }
+ }
+ }
+
+ # De-init the classic dialog assets.
+ $Script:Dialogs.Classic.BannerHeight = $null
+
+ # Initialize the module's global state.
+ $Script:ADT.Environment = & $Script:CommandTable.'New-ADTEnvironmentTable'
+ $Script:ADT.Config = & $Script:CommandTable.'Import-ADTConfig' -BaseDirectory $Script:ADT.Directories.Config
+ $Script:ADT.Language = & $Script:CommandTable.'Get-ADTStringLanguage'
+ $Script:ADT.Strings = & $Script:CommandTable.'Import-ADTModuleDataFile' -BaseDirectory $Script:ADT.Directories.Strings -FileName strings.psd1 -UICulture $Script:ADT.Language -IgnorePolicy
+ $Script:ADT.Sessions.Clear()
+ $Script:ADT.TerminalServerMode = $false
+ $Script:ADT.LastExitCode = 0
+
+ # Calculate how long this process took before finishing.
+ $Script:ADT.Durations.ModuleInit = [System.DateTime]::Now - $moduleInitStart
+ $Script:ADT.Initialized = $true
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Install-ADTMSUpdates
+#
+#-----------------------------------------------------------------------------
+
+function Install-ADTMSUpdates
+{
+ <#
+ .SYNOPSIS
+ Install all Microsoft Updates in a given directory.
+
+ .DESCRIPTION
+ Install all Microsoft Updates of type ".exe", ".msu", or ".msp" in a given directory (recursively search directory). The function will check if the update is already installed and skip it if it is. It handles older redistributables and different types of updates appropriately.
+
+ .PARAMETER Directory
+ Directory containing the updates.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Install-ADTMSUpdates -Directory "$($adtSession.DirFiles)\MSUpdates"
+
+ Installs all Microsoft Updates found in the specified directory.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Install-ADTMSUpdates
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Directory
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $kbPattern = '(?i)kb\d{6,8}'
+ }
+
+ process
+ {
+ # Get all hotfixes and install if required.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Recursively installing all Microsoft Updates in directory [$Directory]."
+ foreach ($file in (& $Script:CommandTable.'Get-ChildItem' -LiteralPath $Directory -Recurse -Include ('*.exe', '*.msu', '*.msp')))
+ {
+ try
+ {
+ try
+ {
+ if ($file.Name -match 'redist')
+ {
+ # Handle older redistributables (ie, VC++ 2005)
+ [System.Version]$redistVersion = $file.VersionInfo.ProductVersion
+ [System.String]$redistDescription = $file.VersionInfo.FileDescription
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Installing [$redistDescription $redistVersion]..."
+ if ($redistDescription -match 'Win32 Cabinet Self-Extractor')
+ {
+ & $Script:CommandTable.'Start-ADTProcess' -FilePath $file.FullName -ArgumentList '/q' -WindowStyle 'Hidden' -IgnoreExitCodes '*'
+ }
+ else
+ {
+ & $Script:CommandTable.'Start-ADTProcess' -FilePath $file.FullName -ArgumentList '/quiet /norestart' -WindowStyle 'Hidden' -IgnoreExitCodes '*'
+ }
+ }
+ elseif ($kbNumber = [System.Text.RegularExpressions.Regex]::Match($file.Name, $kbPattern).ToString())
+ {
+ # Check to see whether the KB is already installed
+ if (& $Script:CommandTable.'Test-ADTMSUpdates' -KbNumber $kbNumber)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "KB Number [$kbNumber] is already installed. Continue..."
+ continue
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "KB Number [$KBNumber] was not detected and will be installed."
+ switch ($file.Extension)
+ {
+ '.exe'
+ {
+ # Installation type for executables (i.e., Microsoft Office Updates).
+ & $Script:CommandTable.'Start-ADTProcess' -FilePath $file.FullName -ArgumentList '/quiet /norestart' -WindowStyle 'Hidden' -IgnoreExitCodes '*'
+ break
+ }
+ '.msu'
+ {
+ # Installation type for Windows updates using Windows Update Standalone Installer.
+ & $Script:CommandTable.'Start-ADTProcess' -FilePath "$([System.Environment]::SystemDirectory)\wusa.exe" -ArgumentList "`"$($file.FullName)`" /quiet /norestart" -WindowStyle 'Hidden' -IgnoreExitCodes '*'
+ break
+ }
+ '.msp'
+ {
+ # Installation type for Windows Installer Patch
+ & $Script:CommandTable.'Start-ADTMsiProcess' -Action 'Patch' -Path $file.FullName -IgnoreExitCodes '*'
+ break
+ }
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Install-ADTSCCMSoftwareUpdates
+#
+#-----------------------------------------------------------------------------
+
+function Install-ADTSCCMSoftwareUpdates
+{
+ <#
+ .SYNOPSIS
+ Scans for outstanding SCCM updates to be installed and installs the pending updates.
+
+ .DESCRIPTION
+ Scans for outstanding SCCM updates to be installed and installs the pending updates.
+
+ Only compatible with SCCM 2012 Client or higher. This function can take several minutes to run.
+
+ .PARAMETER SoftwareUpdatesScanWaitInSeconds
+ The amount of time to wait in seconds for the software updates scan to complete.
+
+ .PARAMETER WaitForPendingUpdatesTimeout
+ The amount of time to wait for missing and pending updates to install before exiting the function.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Install-ADTSCCMSoftwareUpdates
+
+ Scans for outstanding SCCM updates and installs the pending updates with default wait times.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Install-ADTSCCMSoftwareUpdates
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$SoftwareUpdatesScanWaitInSeconds = 180,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$WaitForPendingUpdatesTimeout = [System.TimeSpan]::FromMinutes(45)
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If SCCM 2007 Client or lower, exit function.
+ if (($SCCMClientVersion = & $Script:CommandTable.'Get-ADTSCCMClientVersion').Major -le 4)
+ {
+ $naerParams = @{
+ Exception = [System.Data.VersionNotFoundException]::new('SCCM 2007 or lower, which is incompatible with this function, was detected on this system.')
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'CcmExecVersionLowerThanMinimum'
+ TargetObject = $SCCMClientVersion
+ RecommendedAction = "Please review the installed CcmExec client and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Trigger SCCM client scan for Software Updates.
+ $StartTime = [System.DateTime]::Now
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Triggering SCCM client scan for Software Updates...'
+ & $Script:CommandTable.'Invoke-ADTSCCMTask' -ScheduleID 'SoftwareUpdatesScan'
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The SCCM client scan for Software Updates has been triggered. The script is suspended for [$SoftwareUpdatesScanWaitInSeconds] seconds to let the update scan finish."
+ & $Script:CommandTable.'Start-Sleep' -Seconds $SoftwareUpdatesScanWaitInSeconds
+
+ # Find the number of missing updates.
+ try
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Getting the number of missing updates...'
+ [Microsoft.Management.Infrastructure.CimInstance[]]$CMMissingUpdates = & $Script:CommandTable.'Get-CimInstance' -Namespace ROOT\CCM\ClientSDK -Query "SELECT * FROM CCM_SoftwareUpdate WHERE ComplianceState = '0'"
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to find the number of missing software updates.`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 2
+ throw
+ }
+
+ # Install missing updates and wait for pending updates to finish installing.
+ if (!$CMMissingUpdates.Count)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'There are no missing updates.'
+ return
+ }
+
+ # Install missing updates.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Installing missing updates. The number of missing updates is [$($CMMissingUpdates.Count)]."
+ $null = & $Script:CommandTable.'Invoke-CimMethod' -Namespace ROOT\CCM\ClientSDK -ClassName CCM_SoftwareUpdatesManager -MethodName InstallUpdates -Arguments @{ CCMUpdates = $CMMissingUpdates }
+
+ # Wait for pending updates to finish installing or the timeout value to expire.
+ do
+ {
+ & $Script:CommandTable.'Start-Sleep' -Seconds 60
+ [Microsoft.Management.Infrastructure.CimInstance[]]$CMInstallPendingUpdates = & $Script:CommandTable.'Get-CimInstance' -Namespace ROOT\CCM\ClientSDK -Query 'SELECT * FROM CCM_SoftwareUpdate WHERE EvaluationState = 6 or EvaluationState = 7'
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The number of updates pending installation is [$($CMInstallPendingUpdates.Count)]."
+ }
+ while (($CMInstallPendingUpdates.Count -ne 0) -and ([System.DateTime]::Now - $StartTime) -lt $WaitForPendingUpdatesTimeout)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to trigger installation of missing software updates."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTAllUsersRegistryAction
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTAllUsersRegistryAction
+{
+ <#
+ .SYNOPSIS
+ Set current user registry settings for all current users and any new users in the future.
+
+ .DESCRIPTION
+ Set HKCU registry settings for all current and future users by loading their NTUSER.dat registry hive file, and making the modifications.
+
+ This function will modify HKCU settings for all users even when executed under the SYSTEM account and can be used as an alternative to using ActiveSetup for registry settings.
+
+ To ensure new users in the future get the registry edits, the Default User registry hive used to provision the registry for new users is modified.
+
+ The advantage of using this function over ActiveSetup is that a user does not have to log off and log back on before the changes take effect.
+
+ .PARAMETER ScriptBlock
+ Script block which contains HKCU registry actions to be run for all users on the system.
+
+ .PARAMETER UserProfiles
+ Specify the user profiles to modify HKCU registry settings for. Default is all user profiles except for system profiles.
+
+ .PARAMETER SkipUnloadedProfiles
+ Specifies that unloaded registry hives should be skipped and not be loaded by the function.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Invoke-ADTAllUsersRegistryAction -ScriptBlock {
+ Set-ADTRegistryKey -Key 'HKCU\Software\Microsoft\Office\14.0\Common' -Name 'qmenable' -Value 0 -Type DWord -SID $_.SID
+ Set-ADTRegistryKey -Key 'HKCU\Software\Microsoft\Office\14.0\Common' -Name 'updatereliabilitydata' -Value 1 -Type DWord -SID $_.SID
+ }
+
+ Example demonstrating the setting of two values within each user's HKEY_CURRENT_USER hive.
+
+ .EXAMPLE
+ Invoke-ADTAllUsersRegistryAction {
+ Set-ADTRegistryKey -Key 'HKCU\Software\Microsoft\Office\14.0\Common' -Name 'qmenable' -Value 0 -Type DWord -SID $_.SID
+ Set-ADTRegistryKey -Key 'HKCU\Software\Microsoft\Office\14.0\Common' -Name 'updatereliabilitydata' -Value 1 -Type DWord -SID $_.SID
+ }
+
+ As the previous example, but showing how to use ScriptBlock as a positional parameter with no name specified.
+
+ .EXAMPLE
+ Invoke-ADTAllUsersRegistryAction -UserProfiles (Get-ADTUserProfiles -ExcludeDefaultUser) -ScriptBlock {
+ Set-ADTRegistryKey -Key 'HKCU\Software\Microsoft\Office\14.0\Common' -Name 'qmenable' -Value 0 -Type DWord -SID $_.SID
+ Set-ADTRegistryKey -Key 'HKCU\Software\Microsoft\Office\14.0\Common' -Name 'updatereliabilitydata' -Value 1 -Type DWord -SID $_.SID
+ }
+
+ As the previous example, but sending specific user profiles through to exclude the Default profile.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Invoke-ADTAllUsersRegistryAction
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ScriptBlock[]]$ScriptBlock,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Types.UserProfile[]]$UserProfiles = (& $Script:CommandTable.'Get-ADTUserProfiles'),
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipUnloadedProfiles
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Internal function to unload registry hives at the end of the operation.
+ function Dismount-UserProfileRegistryHive
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Unloading the User [$($UserProfile.NTAccount)] registry hive in path [HKEY_USERS\$($UserProfile.SID)]."
+ $null = & "$([System.Environment]::SystemDirectory)\reg.exe" UNLOAD "HKEY_USERS\$($UserProfile.SID)" 2>&1
+ }
+ }
+
+ process
+ {
+ foreach ($UserProfile in $UserProfiles)
+ {
+ $ManuallyLoadedRegHive = $false
+ try
+ {
+ try
+ {
+ # Set the path to the user's registry hive file.
+ $UserRegistryHiveFile = & $Script:CommandTable.'Join-Path' -Path $UserProfile.ProfilePath -ChildPath 'NTUSER.DAT'
+
+ # Load the User profile registry hive if it is not already loaded because the User is logged in.
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$($UserProfile.SID)"))
+ {
+ # Only load the profile if we've been asked to.
+ if ($SkipUnloadedProfiles)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Skipping User [$($UserProfile.NTAccount)] as the registry hive is not loaded."
+ continue
+ }
+
+ # Load the User registry hive if the registry hive file exists.
+ if (![System.IO.File]::Exists($UserRegistryHiveFile))
+ {
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("Failed to find the registry hive file [$UserRegistryHiveFile] for User [$($UserProfile.NTAccount)] with SID [$($UserProfile.SID)]. Continue...")
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'UserRegistryHiveFileNotFound'
+ TargetObject = $UserRegistryHiveFile
+ RecommendedAction = "Please confirm the state of this user profile and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Loading the User [$($UserProfile.NTAccount)] registry hive in path [HKEY_USERS\$($UserProfile.SID)]."
+ $null = & "$([System.Environment]::SystemDirectory)\reg.exe" LOAD "HKEY_USERS\$($UserProfile.SID)" $UserRegistryHiveFile 2>&1
+ $ManuallyLoadedRegHive = $true
+ }
+
+ # Invoke changes against registry.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Executing scriptblock to modify HKCU registry settings for all users.'
+ & $Script:CommandTable.'ForEach-Object' -InputObject $UserProfile -Begin $null -End $null -Process $ScriptBlock
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to modify the registry hive for User [$($UserProfile.NTAccount)] with SID [$($UserProfile.SID)]`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 3
+ }
+ finally
+ {
+ if ($ManuallyLoadedRegHive)
+ {
+ try
+ {
+ try
+ {
+ Dismount-UserProfileRegistryHive
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "REG.exe failed to unload the registry hive with exit code [$($Global:LASTEXITCODE)] and error message [$($_.Exception.Message)]." -Severity 2
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Performing manual garbage collection to ensure successful unloading of registry hive." -Severity 2
+ [System.GC]::Collect(); [System.GC]::WaitForPendingFinalizers(); [System.Threading.Thread]::Sleep(5000)
+ Dismount-UserProfileRegistryHive
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to unload the registry hive for User [$($UserProfile.NTAccount)] with SID [$($UserProfile.SID)]. REG.exe exit code [$Global:LASTEXITCODE]. Error message: [$($_.Exception.Message)]" -Severity 3
+ }
+ }
+ }
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTCommandWithRetries
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTCommandWithRetries
+{
+ <#
+ .SYNOPSIS
+ Drop-in replacement for any cmdlet/function where a retry is desirable due to transient issues.
+
+ .DESCRIPTION
+ This function invokes the specified cmdlet/function, accepting all of its parameters but retries an operation for the configured value before throwing.
+
+ .PARAMETER Command
+ The name of the command to invoke.
+
+ .PARAMETER Retries
+ How many retries to perform before throwing.
+
+ .PARAMETER SleepSeconds
+ How many seconds to sleep between retries.
+
+ .PARAMETER Parameters
+ A 'ValueFromRemainingArguments' parameter to collect the parameters as would be passed to the provided Command.
+
+ While values can be directly provided to this parameter, it's not designed to be explicitly called.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Object
+
+ Invoke-ADTCommandWithRetries returns the output of the invoked command.
+
+ .EXAMPLE
+ Invoke-ADTCommandWithRetries -Command Invoke-WebRequest -Uri https://aka.ms/getwinget -OutFile "$($adtSession.DirSupportFiles)\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
+
+ Downloads the latest WinGet installer to the SupportFiles directory.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Invoke-ADTCommandWithRetries
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$Command,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$Retries = 3,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateRange(1, 60)]
+ [System.UInt32]$SleepSeconds = 5,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Generic.List[System.Object]]$Parameters
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Attempt to get command from our lookup table.
+ $commandObj = if ($Command -is [System.Management.Automation.CommandInfo])
+ {
+ $Command
+ }
+ elseif ($Script:CommandTable.ContainsKey($Command))
+ {
+ $Script:CommandTable.$Command
+ }
+ else
+ {
+ & $Script:CommandTable.'Get-Command' -Name $Command
+ }
+
+ # Convert the passed parameters into a dictionary for splatting onto the command.
+ $boundParams = & $Script:CommandTable.'Convert-ADTValuesFromRemainingArguments' -RemainingArguments $Parameters
+ $callerName = (& $Script:CommandTable.'Get-PSCallStack')[1].Command
+
+ # Perform the request, and retry it as per the configured values.
+ for ($i = 0; $i -lt $Retries; $i++)
+ {
+ try
+ {
+ return (& $commandObj @boundParams)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The invocation to '$($commandObj.Name)' failed with message: $($_.Exception.Message.TrimEnd('.')). Trying again in $SleepSeconds second$(if (!$SleepSeconds.Equals(1)) {'s'})." -Severity 2 -Source $callerName
+ [System.Threading.Thread]::Sleep($SleepSeconds * 1000)
+ $errorRecord = $_
+ }
+ }
+
+ # If we're here, we failed too many times. Throw the captured ErrorRecord.
+ throw $errorRecord
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTFunctionErrorHandler
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTFunctionErrorHandler
+{
+ <#
+ .SYNOPSIS
+ Handles errors within ADT functions by logging and optionally passing through the error.
+
+ .DESCRIPTION
+ This function handles errors within ADT functions by logging the error message and optionally passing through the error record. It recovers the true ErrorActionPreference set by the caller and sets it within the function. If a log message is provided, it appends the resolved error record to the log message. Depending on the ErrorActionPreference, it either throws a terminating error or writes a non-terminating error.
+
+ .PARAMETER Cmdlet
+ The cmdlet that is calling this function.
+
+ .PARAMETER SessionState
+ The session state of the calling cmdlet.
+
+ .PARAMETER ErrorRecord
+ The error record to handle.
+
+ .PARAMETER LogMessage
+ The error message to write to the active ADTSession's log file.
+
+ .PARAMETER ResolveErrorProperties
+ If specified, the specific ErrorRecord properties to print during resolution.
+
+ .PARAMETER AdditionalResolveErrorProperties
+ If specified, a list of additional ErrorRecord properties to print during resolution.
+
+ .PARAMETER DisableErrorResolving
+ If specified, the function will not append the resolved error record to the log message.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Invoke-ADTFunctionErrorHandler -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+
+ Handles the error within the calling cmdlet and logs it.
+
+ .EXAMPLE
+ Invoke-ADTFunctionErrorHandler -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "An error occurred" -DisableErrorResolving
+
+ Handles the error within the calling cmdlet, logs a custom message without resolving the error record, and logs it.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Invoke-ADTFunctionErrorHandler
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'None')]
+ [OutputType([System.Management.Automation.ErrorRecord])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.PSCmdlet]$Cmdlet,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SessionState]$SessionState,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ErrorRecord]$ErrorRecord,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LogMessage,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'ResolveErrorProperties')]
+ [ValidateNotNullOrEmpty()]
+ [SupportsWildcards()]
+ [System.String[]]$ResolveErrorProperties,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'AdditionalResolveErrorProperties')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$AdditionalResolveErrorProperties,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'DisableErrorResolving')]
+ [System.Management.Automation.SwitchParameter]$DisableErrorResolving
+ )
+
+ # Recover true ErrorActionPreference the caller may have set,
+ # unless an ErrorAction was specifically provided to this function.
+ $ErrorActionPreference = if ($PSBoundParameters.ContainsKey('ErrorAction'))
+ {
+ $PSBoundParameters.ErrorAction
+ }
+ elseif ($SessionState.Equals($ExecutionContext.SessionState))
+ {
+ & $Script:CommandTable.'Get-Variable' -Name OriginalErrorAction -Scope 1 -ValueOnly
+ }
+ else
+ {
+ $SessionState.PSVariable.Get('OriginalErrorAction').Value
+ }
+
+ # If the caller hasn't specified a LogMessage, use the ErrorRecord's message.
+ if ([System.String]::IsNullOrWhiteSpace($LogMessage))
+ {
+ $LogMessage = $ErrorRecord.Exception.Message
+ }
+
+ # Write-Error enforces its own name against the Activity, let's re-write it.
+ if ($ErrorRecord.CategoryInfo.Activity -match '^Write-Error$')
+ {
+ $ErrorRecord.CategoryInfo.Activity = $Cmdlet.MyInvocation.MyCommand.Name
+ }
+
+ # Write out the error to the log file.
+ if (!$DisableErrorResolving)
+ {
+ $raerProps = @{ ErrorRecord = $ErrorRecord }; if ($PSCmdlet.ParameterSetName.Equals('AdditionalResolveErrorProperties'))
+ {
+ $raerProps.Add('Property', $($Script:CommandTable.'Resolve-ADTErrorRecord'.ScriptBlock.Ast.Body.ParamBlock.Parameters.Where({ $_.Name.VariablePath.UserPath.Equals('Property') }).DefaultValue.Pipeline.PipelineElements.Expression.Elements.Value; $AdditionalResolveErrorProperties))
+ }
+ elseif ($PSCmdlet.ParameterSetName.Equals('ResolveErrorProperties'))
+ {
+ $raerProps.Add('Property', $ResolveErrorProperties)
+ }
+ $LogMessage += "`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' @raerProps)"
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message $LogMessage -Source $Cmdlet.MyInvocation.MyCommand.Name -Severity 3
+
+ # If we're stopping, throw a terminating error. While WriteError will terminate if stopping,
+ # this can also write out an [System.Management.Automation.ActionPreferenceStopException] object.
+ if ($ErrorActionPreference.Equals([System.Management.Automation.ActionPreference]::Stop))
+ {
+ $Cmdlet.ThrowTerminatingError($ErrorRecord)
+ }
+ elseif (!(& $Script:CommandTable.'Test-ADTSessionActive') -or ($ErrorActionPreference -notmatch '^(SilentlyContinue|Ignore)$'))
+ {
+ $Cmdlet.WriteError($ErrorRecord)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTObjectMethod
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTObjectMethod
+{
+ <#
+ .SYNOPSIS
+ Invoke method on any object.
+
+ .DESCRIPTION
+ Invoke method on any object with or without using named parameters.
+
+ .PARAMETER InputObject
+ Specifies an object which has methods that can be invoked.
+
+ .PARAMETER MethodName
+ Specifies the name of a method to invoke.
+
+ .PARAMETER ArgumentList
+ Argument to pass to the method being executed. Allows execution of method without specifying named parameters.
+
+ .PARAMETER Parameter
+ Argument to pass to the method being executed. Allows execution of method by using named parameters.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Object
+
+ The object returned by the method being invoked.
+
+ .EXAMPLE
+ PS C:\>$ShellApp = New-Object -ComObject 'Shell.Application'
+ PS C:\>$null = Invoke-ADTObjectMethod -InputObject $ShellApp -MethodName 'MinimizeAll'
+
+ Minimizes all windows.
+
+ .EXAMPLE
+ PS C:\>$ShellApp = New-Object -ComObject 'Shell.Application'
+ PS C:\>$null = Invoke-ADTObjectMethod -InputObject $ShellApp -MethodName 'Explore' -Parameter @{'vDir'='C:\Windows'}
+
+ Opens the C:\Windows folder in a Windows Explorer window.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Invoke-ADTObjectMethod
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'Positional')]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$InputObject,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$MethodName,
+
+ [Parameter(Mandatory = $false, Position = 2, ParameterSetName = 'Positional')]
+ [ValidateNotNullOrEmpty()]
+ [System.Object[]]$ArgumentList,
+
+ [Parameter(Mandatory = $true, Position = 2, ParameterSetName = 'Named')]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Hashtable]$Parameter
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ switch ($PSCmdlet.ParameterSetName)
+ {
+ Named
+ {
+ # Invoke method by using parameter names.
+ return $InputObject.GetType().InvokeMember($MethodName, [System.Reflection.BindingFlags]::InvokeMethod, $null, $InputObject, ([System.Object[]]$Parameter.Values), $null, $null, ([System.String[]]$Parameter.Keys))
+ }
+ Positional
+ {
+ # Invoke method without using parameter names.
+ return $InputObject.GetType().InvokeMember($MethodName, [System.Reflection.BindingFlags]::InvokeMethod, $null, $InputObject, $ArgumentList, $null, $null, $null)
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTRegSvr32
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTRegSvr32
+{
+ <#
+ .SYNOPSIS
+ Register or unregister a DLL file.
+
+ .DESCRIPTION
+ Register or unregister a DLL file using regsvr32.exe. This function determines the bitness of the DLL file and uses the appropriate version of regsvr32.exe to perform the action. It supports both 32-bit and 64-bit DLL files on corresponding operating systems.
+
+ .PARAMETER FilePath
+ Path to the DLL file.
+
+ .PARAMETER Action
+ Specify whether to register or unregister the DLL.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return objects.
+
+ .EXAMPLE
+ Invoke-ADTRegSvr32 -FilePath "C:\Test\DcTLSFileToDMSComp.dll" -Action 'Register'
+
+ Registers the specified DLL file.
+
+ .EXAMPLE
+ Invoke-ADTRegSvr32 -FilePath "C:\Test\DcTLSFileToDMSComp.dll" -Action 'Unregister'
+
+ Unregisters the specified DLL file.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Invoke-ADTRegSvr32
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_) -and ([System.IO.Path]::GetExtension($_) -ne '.dll'))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist or is not a DLL file.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('Register', 'Unregister')]
+ [System.String]$Action
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Define parameters to pass to regsrv32.exe.
+ $ActionParameters = switch ($Action = $Host.CurrentCulture.TextInfo.ToTitleCase($Action.ToLower()))
+ {
+ Register
+ {
+ "/s `"$FilePath`""
+ break
+ }
+ Unregister
+ {
+ "/s /u `"$FilePath`""
+ break
+ }
+ }
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$Action DLL file [$FilePath]."
+ try
+ {
+ try
+ {
+ # Determine the bitness of the DLL file.
+ if ((($DLLFileBitness = & $Script:CommandTable.'Get-ADTPEFileArchitecture' -FilePath $FilePath) -ne [PSADT.Shared.SystemArchitecture]::AMD64) -and ($DLLFileBitness -ne [PSADT.Shared.SystemArchitecture]::i386))
+ {
+ $naerParams = @{
+ Exception = [System.PlatformNotSupportedException]::new("File [$filePath] has a detected file architecture of [$DLLFileBitness]. Only 32-bit or 64-bit DLL files can be $($Action.ToLower() + 'ed').")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'DllFileArchitectureError'
+ TargetObject = $FilePath
+ RecommendedAction = "Please review the supplied DLL FilePath and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Get the correct path to regsrv32.exe for the system and DLL file.
+ $RegSvr32Path = if ([System.Environment]::Is64BitOperatingSystem)
+ {
+ if ($DLLFileBitness -eq [PSADT.Shared.SystemArchitecture]::AMD64)
+ {
+ if ([System.Environment]::Is64BitProcess)
+ {
+ "$([System.Environment]::SystemDirectory)\regsvr32.exe"
+ }
+ else
+ {
+ "$([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Windows))\sysnative\regsvr32.exe"
+ }
+ }
+ elseif ($DLLFileBitness -eq [PSADT.Shared.SystemArchitecture]::i386)
+ {
+ "$([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::SystemX86))\regsvr32.exe"
+ }
+ }
+ elseif ($DLLFileBitness -eq [PSADT.Shared.SystemArchitecture]::i386)
+ {
+ "$([System.Environment]::SystemDirectory)\regsvr32.exe"
+ }
+ else
+ {
+ $naerParams = @{
+ Exception = [System.PlatformNotSupportedException]::new("File [$filePath] cannot be $($Action.ToLower()) because it is a 64-bit file on a 32-bit operating system.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'DllFileArchitectureError'
+ TargetObject = $FilePath
+ RecommendedAction = "Please review the supplied DLL FilePath and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Register the DLL file and measure the success.
+ if (($ExecuteResult = & $Script:CommandTable.'Start-ADTProcess' -FilePath $RegSvr32Path -ArgumentList $ActionParameters -WindowStyle Hidden -PassThru).ExitCode -ne 0)
+ {
+ if ($ExecuteResult.ExitCode -eq 60002)
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Start-ADTProcess function failed with exit code [$($ExecuteResult.ExitCode)].")
+ Category = [System.Management.Automation.ErrorCategory]::OperationStopped
+ ErrorId = 'ProcessInvocationError'
+ TargetObject = "$FilePath $ActionParameters"
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ else
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("regsvr32.exe failed with exit code [$($ExecuteResult.ExitCode)].")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'ProcessInvocationError'
+ TargetObject = "$FilePath $ActionParameters"
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to $($Action.ToLower()) DLL file."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Invoke-ADTSCCMTask
+#
+#-----------------------------------------------------------------------------
+
+function Invoke-ADTSCCMTask
+{
+ <#
+ .SYNOPSIS
+ Triggers SCCM to invoke the requested schedule task ID.
+
+ .DESCRIPTION
+ Triggers SCCM to invoke the requested schedule task ID. This function supports a variety of schedule IDs compatible with different versions of the SCCM client. It ensures that the correct schedule IDs are used based on the SCCM client version.
+
+ .PARAMETER ScheduleId
+ Name of the schedule id to trigger.
+
+ Options: HardwareInventory, SoftwareInventory, HeartbeatDiscovery, SoftwareInventoryFileCollection, RequestMachinePolicy, EvaluateMachinePolicy, LocationServicesCleanup, SoftwareMeteringReport, SourceUpdate, PolicyAgentCleanup, RequestMachinePolicy2, CertificateMaintenance, PeerDistributionPointStatus, PeerDistributionPointProvisioning, ComplianceIntervalEnforcement, SoftwareUpdatesAgentAssignmentEvaluation, UploadStateMessage, StateMessageManager, SoftwareUpdatesScan, AMTProvisionCycle, UpdateStorePolicy, StateSystemBulkSend, ApplicationManagerPolicyAction, PowerManagementStartSummarizer
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Invoke-ADTSCCMTask -ScheduleId 'SoftwareUpdatesScan'
+
+ Triggers the 'SoftwareUpdatesScan' schedule task in SCCM.
+
+ .EXAMPLE
+ Invoke-ADTSCCMTask -ScheduleId 'HardwareInventory'
+
+ Triggers the 'HardwareInventory' schedule task in SCCM.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Invoke-ADTSCCMTask
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('HardwareInventory', 'SoftwareInventory', 'HeartbeatDiscovery', 'SoftwareInventoryFileCollection', 'RequestMachinePolicy', 'EvaluateMachinePolicy', 'LocationServicesCleanup', 'SoftwareMeteringReport', 'SourceUpdate', 'PolicyAgentCleanup', 'RequestMachinePolicy2', 'CertificateMaintenance', 'PeerDistributionPointStatus', 'PeerDistributionPointProvisioning', 'ComplianceIntervalEnforcement', 'SoftwareUpdatesAgentAssignmentEvaluation', 'UploadStateMessage', 'StateMessageManager', 'SoftwareUpdatesScan', 'AMTProvisionCycle', 'UpdateStorePolicy', 'StateSystemBulkSend', 'ApplicationManagerPolicyAction', 'PowerManagementStartSummarizer')]
+ [System.String]$ScheduleID
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Create a hashtable of Schedule IDs compatible with SCCM Client 2007.
+ $ScheduleIds = @{
+ HardwareInventory = '{00000000-0000-0000-0000-000000000001}' # Hardware Inventory Collection Task
+ SoftwareInventory = '{00000000-0000-0000-0000-000000000002}' # Software Inventory Collection Task
+ HeartbeatDiscovery = '{00000000-0000-0000-0000-000000000003}' # Heartbeat Discovery Cycle
+ SoftwareInventoryFileCollection = '{00000000-0000-0000-0000-000000000010}' # Software Inventory File Collection Task
+ RequestMachinePolicy = '{00000000-0000-0000-0000-000000000021}' # Request Machine Policy Assignments
+ EvaluateMachinePolicy = '{00000000-0000-0000-0000-000000000022}' # Evaluate Machine Policy Assignments
+ RefreshDefaultMp = '{00000000-0000-0000-0000-000000000023}' # Refresh Default MP Task
+ RefreshLocationServices = '{00000000-0000-0000-0000-000000000024}' # Refresh Location Services Task
+ LocationServicesCleanup = '{00000000-0000-0000-0000-000000000025}' # Location Services Cleanup Task
+ SoftwareMeteringReport = '{00000000-0000-0000-0000-000000000031}' # Software Metering Report Cycle
+ SourceUpdate = '{00000000-0000-0000-0000-000000000032}' # Source Update Manage Update Cycle
+ PolicyAgentCleanup = '{00000000-0000-0000-0000-000000000040}' # Policy Agent Cleanup Cycle
+ RequestMachinePolicy2 = '{00000000-0000-0000-0000-000000000042}' # Request Machine Policy Assignments
+ CertificateMaintenance = '{00000000-0000-0000-0000-000000000051}' # Certificate Maintenance Cycle
+ PeerDistributionPointStatus = '{00000000-0000-0000-0000-000000000061}' # Peer Distribution Point Status Task
+ PeerDistributionPointProvisioning = '{00000000-0000-0000-0000-000000000062}' # Peer Distribution Point Provisioning Status Task
+ ComplianceIntervalEnforcement = '{00000000-0000-0000-0000-000000000071}' # Compliance Interval Enforcement
+ SoftwareUpdatesAgentAssignmentEvaluation = '{00000000-0000-0000-0000-000000000108}' # Software Updates Agent Assignment Evaluation Cycle
+ UploadStateMessage = '{00000000-0000-0000-0000-000000000111}' # Send Unsent State Messages
+ StateMessageManager = '{00000000-0000-0000-0000-000000000112}' # State Message Manager Task
+ SoftwareUpdatesScan = '{00000000-0000-0000-0000-000000000113}' # Force Update Scan
+ AMTProvisionCycle = '{00000000-0000-0000-0000-000000000120}' # AMT Provision Cycle
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If SCCM 2012 Client or higher, modify hashtabe containing Schedule IDs so that it only has the ones compatible with this version of the SCCM client.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invoke SCCM Schedule Task ID [$ScheduleId]..."
+ if ((& $Script:CommandTable.'Get-ADTSCCMClientVersion').Major -ge 5)
+ {
+ $ScheduleIds.Remove('PeerDistributionPointStatus')
+ $ScheduleIds.Remove('PeerDistributionPointProvisioning')
+ $ScheduleIds.Remove('ComplianceIntervalEnforcement')
+ $ScheduleIds.Add('UpdateStorePolicy', '{00000000-0000-0000-0000-000000000114}') # Update Store Policy
+ $ScheduleIds.Add('StateSystemBulkSend', '{00000000-0000-0000-0000-000000000116}') # State System Policy Bulk Send Low
+ $ScheduleIds.Add('ApplicationManagerPolicyAction', '{00000000-0000-0000-0000-000000000121}') # Application Manager Policy Action
+ $ScheduleIds.Add('PowerManagementStartSummarizer', '{00000000-0000-0000-0000-000000000131}') # Power Management Start Summarizer
+ }
+
+ # Determine if the requested Schedule ID is available on this version of the SCCM Client.
+ if (!$ScheduleIds.ContainsKey($ScheduleId))
+ {
+ $naerParams = @{
+ Exception = [System.ApplicationException]::new("The requested ScheduleId [$ScheduleId] is not available with this version of the SCCM Client [$SCCMClientVersion].")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidData
+ ErrorId = 'CcmExecInvalidScheduleId'
+ RecommendedAction = 'Please check the supplied ScheduleId and try again.'
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Trigger SCCM task.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Triggering SCCM Task ID [$ScheduleId]."
+ $null = & $Script:CommandTable.'Invoke-CimMethod' -Namespace ROOT\CCM -ClassName SMS_Client -MethodName TriggerSchedule -Arguments @{ sScheduleID = $ScheduleIds.$ScheduleID }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to trigger SCCM Schedule Task ID [$($ScheduleIds.$ScheduleId)]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Mount-ADTWimFile
+#
+#-----------------------------------------------------------------------------
+
+function Mount-ADTWimFile
+{
+ <#
+ .SYNOPSIS
+ Mounts a WIM file to a specified directory.
+
+ .DESCRIPTION
+ Mounts a WIM file to a specified directory. The function supports mounting by image index or image name. It also provides options to forcefully remove existing directories and return the mounted image details.
+
+ .PARAMETER ImagePath
+ Path to the WIM file to be mounted.
+
+ .PARAMETER Path
+ Directory where the WIM file will be mounted. The directory must be empty and not have a pre-existing WIM mounted.
+
+ .PARAMETER Index
+ Index of the image within the WIM file to be mounted.
+
+ .PARAMETER Name
+ Name of the image within the WIM file to be mounted.
+
+ .PARAMETER Force
+ Forces the removal of the existing directory if it is not empty.
+
+ .PARAMETER PassThru
+ If specified, the function will return the results from `Mount-WindowsImage`.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ Microsoft.Dism.Commands.ImageObject
+
+ Returns the mounted image details if the PassThru parameter is specified.
+
+ .EXAMPLE
+ Mount-ADTWimFile -ImagePath 'C:\Images\install.wim' -Path 'C:\Mount' -Index 1
+
+ Mounts the first image in the 'install.wim' file to the 'C:\Mount' directory.
+
+ .EXAMPLE
+ Mount-ADTWimFile -ImagePath 'C:\Images\install.wim' -Path 'C:\Mount' -Name 'Windows 10 Pro'
+
+ Mounts the image named 'Windows 10 Pro' in the 'install.wim' file to the 'C:\Mount' directory.
+
+ .EXAMPLE
+ Mount-ADTWimFile -ImagePath 'C:\Images\install.wim' -Path 'C:\Mount' -Index 1 -Force
+
+ Mounts the first image in the 'install.wim' file to the 'C:\Mount' directory, forcefully removing the existing directory if it is not empty.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Mount-ADTWimFile
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Index')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'Name')]
+ [ValidateScript({
+ if ($null -eq $_)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ImagePath -ProvidedValue $_ -ExceptionMessage 'The specified input is null.'))
+ }
+ if (!$_.Exists)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ImagePath -ProvidedValue $_ -ExceptionMessage 'The specified image path cannot be found.'))
+ }
+ if ([System.Uri]::new($_).IsUnc)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ImagePath -ProvidedValue $_ -ExceptionMessage 'The specified image path cannot be a network share.'))
+ }
+ return !!$_
+ })]
+ [System.IO.FileInfo]$ImagePath,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Index')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'Name')]
+ [ValidateScript({
+ if ($null -eq $_)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified input is null.'))
+ }
+ if ([System.Uri]::new($_).IsUnc)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified mount path cannot be a network share.'))
+ }
+ if (& $Script:CommandTable.'Get-ADTMountedWimFile' -Path $_)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified mount path has a pre-existing WIM mounted.'))
+ }
+ if (& $Script:CommandTable.'Get-ChildItem' -LiteralPath $_ -ErrorAction Ignore)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified mount path is not empty.'))
+ }
+ return !!$_
+ })]
+ [System.IO.DirectoryInfo]$Path,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Index')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$Index,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Name')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Index')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'Name')]
+ [System.Management.Automation.SwitchParameter]$Force,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Index')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'Name')]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ # Attempt to get specified WIM image before initialising.
+ $null = try
+ {
+ $PSBoundParameters.Remove('PassThru')
+ $PSBoundParameters.Remove('Force')
+ $PSBoundParameters.Remove('Path')
+ & $Script:CommandTable.'Get-WindowsImage' @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ # Announce commencement.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Mounting WIM file [$ImagePath] to [$Path]."
+ try
+ {
+ try
+ {
+ # Provide a warning if this WIM file is already mounted.
+ if (($wimFile = & $Script:CommandTable.'Get-ADTMountedWimFile' -ImagePath $ImagePath))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The WIM file [$ImagePath] is already mounted at [$($wimFile.Path)] and will be mounted again." -Severity 2
+ }
+
+ # If we're using the force, forcibly remove the existing directory.
+ if ([System.IO.Directory]::Exists($Path) -and $Force)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing pre-existing path [$Path] as [-Force] was provided."
+ & $Script:CommandTable.'Remove-Item' -LiteralPath $Path -Force -Confirm:$false
+ }
+
+ # If the path doesn't exist, create it.
+ if (![System.IO.Directory]::Exists($Path))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating path [$Path] as it does not exist."
+ $Path = [System.IO.Directory]::CreateDirectory($Path).FullName
+ }
+
+ # Mount the WIM file.
+ $res = & $Script:CommandTable.'Mount-WindowsImage' @PSBoundParameters -Path $Path -ReadOnly -CheckIntegrity
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Successfully mounted WIM file [$ImagePath]."
+
+ # Store the result within the user's ADTSession if there's an active one.
+ if (& $Script:CommandTable.'Test-ADTSessionActive')
+ {
+ (& $Script:CommandTable.'Get-ADTSession').AddMountedWimFile($ImagePath)
+ }
+
+ # Return the result if we're passing through.
+ if ($PassThru)
+ {
+ return $res
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage 'Error occurred while attemping to mount WIM file.'
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTErrorRecord
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTErrorRecord
+{
+ <#
+ .SYNOPSIS
+ Creates a new ErrorRecord object.
+
+ .DESCRIPTION
+ This function creates a new ErrorRecord object with the specified exception, error category, and optional parameters. It allows for detailed error information to be captured and returned to the caller, who can then throw the error.
+
+ .PARAMETER Exception
+ The exception object that caused the error.
+
+ .PARAMETER Category
+ The category of the error.
+
+ .PARAMETER ErrorId
+ The identifier for the error. Default is 'NotSpecified'.
+
+ .PARAMETER TargetObject
+ The target object that the error is related to.
+
+ .PARAMETER TargetName
+ The name of the target that the error is related to.
+
+ .PARAMETER TargetType
+ The type of the target that the error is related to.
+
+ .PARAMETER Activity
+ The activity that was being performed when the error occurred.
+
+ .PARAMETER Reason
+ The reason for the error.
+
+ .PARAMETER RecommendedAction
+ The recommended action to resolve the error.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Management.Automation.ErrorRecord
+
+ This function returns an ErrorRecord object.
+
+ .EXAMPLE
+ PS C:\>$exception = [System.Exception]::new("An error occurred.")
+ PS C:\>$category = [System.Management.Automation.ErrorCategory]::NotSpecified
+ PS C:\>New-ADTErrorRecord -Exception $exception -Category $category -ErrorId "CustomErrorId" -TargetObject $null -TargetName "TargetName" -TargetType "TargetType" -Activity "Activity" -Reason "Reason" -RecommendedAction "RecommendedAction"
+
+ Creates a new ErrorRecord object with the specified parameters.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/New-ADTErrorRecord
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This function does not change system state.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ [OutputType([System.Management.Automation.ErrorRecord])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Exception]$Exception,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ErrorCategory]$Category,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ErrorId = 'NotSpecified',
+
+ [Parameter(Mandatory = $false)]
+ [AllowNull()]
+ [System.Object]$TargetObject,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TargetName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TargetType,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Activity,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Reason,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$RecommendedAction
+ )
+
+ # Instantiate new ErrorRecord object.
+ $errRecord = [System.Management.Automation.ErrorRecord]::new($Exception, $ErrorId, $Category, $TargetObject)
+
+ # Add in all optional values, if specified.
+ if ($Activity)
+ {
+ $errRecord.CategoryInfo.Activity = $Activity
+ }
+ if ($TargetName)
+ {
+ $errRecord.CategoryInfo.TargetName = $TargetName
+ }
+ if ($TargetType)
+ {
+ $errRecord.CategoryInfo.TargetType = $TargetType
+ }
+ if ($Reason)
+ {
+ $errRecord.CategoryInfo.Reason = $Reason
+ }
+ if ($RecommendedAction)
+ {
+ $errRecord.ErrorDetails = [System.Management.Automation.ErrorDetails]::new($errRecord.Exception.Message)
+ $errRecord.ErrorDetails.RecommendedAction = $RecommendedAction
+ }
+
+ # Return the ErrorRecord to the caller, who will then throw it.
+ return $errRecord
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTFolder
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTFolder
+{
+ <#
+ .SYNOPSIS
+ Create a new folder.
+
+ .DESCRIPTION
+ Create a new folder if it does not exist. This function checks if the specified path already exists and creates the folder if it does not. It logs the creation process and handles any errors that may occur during the folder creation.
+
+ .PARAMETER Path
+ Path to the new folder to create.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ New-ADTFolder -Path "$env:WinDir\System32"
+
+ Creates a new folder at the specified path if it does not already exist.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/New-ADTFolder
+ #>
+
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ if ([System.IO.Directory]::Exists($Path))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Folder [$Path] already exists."
+ return
+ }
+
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating folder [$Path]."
+ $null = & $Script:CommandTable.'New-Item' -Path $Path -ItemType Directory -Force
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to create folder [$Path]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTMsiTransform
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTMsiTransform
+{
+ <#
+ .SYNOPSIS
+ Create a transform file for an MSI database.
+
+ .DESCRIPTION
+ Create a transform file for an MSI database and create/modify properties in the Properties table. This function allows you to specify an existing transform to apply before making changes and to define the path for the new transform file. If the new transform file already exists, it will be deleted before creating a new one.
+
+ .PARAMETER MsiPath
+ Specify the path to an MSI file.
+
+ .PARAMETER ApplyTransformPath
+ Specify the path to a transform which should be applied to the MSI database before any new properties are created or modified.
+
+ .PARAMETER NewTransformPath
+ Specify the path where the new transform file with the desired properties will be created. If a transform file of the same name already exists, it will be deleted before a new one is created.
+
+ .PARAMETER TransformProperties
+ Hashtable which contains calls to `Set-ADTMsiProperty` for configuring the desired properties which should be included in the new transform file.
+
+ Example hashtable: @{ ALLUSERS = 1 }
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ New-ADTMsiTransform -MsiPath 'C:\Temp\PSADTInstall.msi' -TransformProperties @{
+ ALLUSERS = 1
+ AgreeToLicense = 'Yes'
+ REBOOT = 'ReallySuppress'
+ RebootYesNo = 'No'
+ ROOTDRIVE = 'C:'
+ }
+
+ Creates a new transform file for the specified MSI with the given properties.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/New-ADTMsiTransform
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This function does not change system state.")]
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (!(& $Script:CommandTable.'Test-Path' -Path $_ -PathType Leaf))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName MsiPath -ProvidedValue $_ -ExceptionMessage 'The specified path does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$MsiPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if (!(& $Script:CommandTable.'Test-Path' -Path $_ -PathType Leaf))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ApplyTransformPath -ProvidedValue $_ -ExceptionMessage 'The specified path does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$ApplyTransformPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = 'If `-ApplyTransformPath` was specified: `.new.mst`; If only `-MsiPath` was specified: `.mst`')]
+ [System.String]$NewTransformPath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.Hashtable]$TransformProperties
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Define properties for how the MSI database is opened.
+ $msiOpenDatabaseTypes = @{
+ OpenDatabaseModeReadOnly = 0
+ OpenDatabaseModeTransact = 1
+ ViewModifyUpdate = 2
+ ViewModifyReplace = 4
+ ViewModifyDelete = 6
+ TransformErrorNone = 0
+ TransformValidationNone = 0
+ SuppressApplyTransformErrors = 63
+ }
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating a transform file for MSI [$MsiPath]."
+ try
+ {
+ try
+ {
+ # Create a second copy of the MSI database.
+ $MsiParentFolder = & $Script:CommandTable.'Split-Path' -Path $MsiPath -Parent
+ $TempMsiPath = & $Script:CommandTable.'Join-Path' -Path $MsiParentFolder -ChildPath ([System.IO.Path]::GetRandomFileName())
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Copying MSI database in path [$MsiPath] to destination [$TempMsiPath]."
+ $null = & $Script:CommandTable.'Copy-Item' -LiteralPath $MsiPath -Destination $TempMsiPath -Force
+
+ # Open both copies of the MSI database.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Opening the MSI database [$MsiPath] in read only mode."
+ $Installer = & $Script:CommandTable.'New-Object' -ComObject WindowsInstaller.Installer
+ $MsiPathDatabase = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Installer -MethodName OpenDatabase -ArgumentList @($MsiPath, $msiOpenDatabaseTypes.OpenDatabaseModeReadOnly)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Opening the MSI database [$TempMsiPath] in view/modify/update mode."
+ $TempMsiPathDatabase = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Installer -MethodName OpenDatabase -ArgumentList @($TempMsiPath, $msiOpenDatabaseTypes.ViewModifyUpdate)
+
+ # If a MSI transform file was specified, then apply it to the temporary copy of the MSI database.
+ if ($ApplyTransformPath)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Applying transform file [$ApplyTransformPath] to MSI database [$TempMsiPath]."
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $TempMsiPathDatabase -MethodName ApplyTransform -ArgumentList @($ApplyTransformPath, $msiOpenDatabaseTypes.SuppressApplyTransformErrors)
+ }
+
+ # Determine the path for the new transform file that will be generated.
+ if (!$NewTransformPath)
+ {
+ $NewTransformFileName = if ($ApplyTransformPath)
+ {
+ [System.IO.Path]::GetFileNameWithoutExtension($ApplyTransformPath) + '.new' + [System.IO.Path]::GetExtension($ApplyTransformPath)
+ }
+ else
+ {
+ [System.IO.Path]::GetFileNameWithoutExtension($MsiPath) + '.mst'
+ }
+ $NewTransformPath = & $Script:CommandTable.'Join-Path' -Path $MsiParentFolder -ChildPath $NewTransformFileName
+ }
+
+ # Set the MSI properties in the temporary copy of the MSI database.
+ foreach ($property in $TransformProperties.GetEnumerator())
+ {
+ & $Script:CommandTable.'Set-ADTMsiProperty' -Database $TempMsiPathDatabase -PropertyName $property.Key -PropertyValue $property.Value
+ }
+
+ # Commit the new properties to the temporary copy of the MSI database
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $TempMsiPathDatabase -MethodName Commit
+
+ # Reopen the temporary copy of the MSI database in read only mode.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Re-opening the MSI database [$TempMsiPath] in read only mode."
+ $null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($TempMsiPathDatabase)
+ $TempMsiPathDatabase = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Installer -MethodName OpenDatabase -ArgumentList @($TempMsiPath, $msiOpenDatabaseTypes.OpenDatabaseModeReadOnly)
+
+ # Delete the new transform file path if it already exists.
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath $NewTransformPath -PathType Leaf)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "A transform file of the same name already exists. Deleting transform file [$NewTransformPath]."
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $NewTransformPath -Force
+ }
+
+ # Generate the new transform file by taking the difference between the temporary copy of the MSI database and the original MSI database.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Generating new transform file [$NewTransformPath]."
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $TempMsiPathDatabase -MethodName GenerateTransform -ArgumentList @($MsiPathDatabase, $NewTransformPath)
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $TempMsiPathDatabase -MethodName CreateTransformSummaryInfo -ArgumentList @($MsiPathDatabase, $NewTransformPath, $msiOpenDatabaseTypes.TransformErrorNone, $msiOpenDatabaseTypes.TransformValidationNone)
+
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $NewTransformPath -PathType Leaf))
+ {
+ $naerParams = @{
+ Exception = [System.IO.IOException]::new("Failed to generate transform file in path [$NewTransformPath].")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'MsiTransformFileMissing'
+ TargetObject = $NewTransformPath
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Successfully created new transform file in path [$NewTransformPath]."
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to create new transform file in path [$NewTransformPath]."
+ }
+ finally
+ {
+ # Release all COM objects to prevent file locks.
+ $null = foreach ($variable in (& $Script:CommandTable.'Get-Variable' -Name TempMsiPathDatabase, MsiPathDatabase, Installer -ValueOnly -ErrorAction Ignore))
+ {
+ try
+ {
+ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($variable)
+ }
+ catch
+ {
+ $null
+ }
+ }
+
+ # Delete the temporary copy of the MSI database.
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $TempMsiPath -Force -ErrorAction Ignore
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTShortcut
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTShortcut
+{
+ <#
+ .SYNOPSIS
+ Creates a new .lnk or .url type shortcut.
+
+ .DESCRIPTION
+ Creates a new shortcut .lnk or .url file, with configurable options. This function allows you to specify various parameters such as the target path, arguments, icon location, description, working directory, window style, run as administrator, and hotkey.
+
+ .PARAMETER Path
+ Path to save the shortcut.
+
+ .PARAMETER TargetPath
+ Target path or URL that the shortcut launches.
+
+ .PARAMETER Arguments
+ Arguments to be passed to the target path.
+
+ .PARAMETER IconLocation
+ Location of the icon used for the shortcut.
+
+ .PARAMETER IconIndex
+ The index of the icon. Executables, DLLs, ICO files with multiple icons need the icon index to be specified. This parameter is an Integer. The first index is 0.
+
+ .PARAMETER Description
+ Description of the shortcut.
+
+ .PARAMETER WorkingDirectory
+ Working Directory to be used for the target path.
+
+ .PARAMETER WindowStyle
+ Windows style of the application. Options: Normal, Maximized, Minimized.
+
+ .PARAMETER RunAsAdmin
+ Set shortcut to run program as administrator. This option will prompt user to elevate when executing shortcut.
+
+ .PARAMETER Hotkey
+ Create a Hotkey to launch the shortcut, e.g. "CTRL+SHIFT+F".
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ New-ADTShortcut -Path "$envCommonStartMenuPrograms\My Shortcut.lnk" -TargetPath "$envWinDir\notepad.exe" -IconLocation "$envWinDir\notepad.exe" -Description 'Notepad' -WorkingDirectory '%HOMEDRIVE%\%HOMEPATH%'
+
+ Creates a new shortcut for Notepad with the specified parameters.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Url shortcuts only support TargetPath, IconLocation and IconIndex. Other parameters are ignored.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/New-ADTShortcut
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateScript({
+ if (![System.IO.Path]::GetExtension($_).ToLower().Equals('.lnk') -and ![System.IO.Path]::GetExtension($_).ToLower().Equals('.url'))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified path does not have the correct extension.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TargetPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Arguments,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$IconLocation,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$IconIndex,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Description,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Normal', 'Maximized', 'Minimized')]
+ [System.String]$WindowStyle = 'Normal',
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$RunAsAdmin,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Hotkey
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ # Make sure .NET's current directory is synced with PowerShell's.
+ try
+ {
+ try
+ {
+ [System.IO.Directory]::SetCurrentDirectory((& $Script:CommandTable.'Get-Location' -PSProvider FileSystem).ProviderPath)
+ $FullPath = [System.IO.Path]::GetFullPath($Path)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Specified path [$Path] is not valid."
+ return
+ }
+
+ try
+ {
+ try
+ {
+ # Make sure directory is present before continuing.
+ if (!($PathDirectory = [System.IO.Path]::GetDirectoryName($FullPath)))
+ {
+ # The path is root or no filename supplied.
+ if (![System.IO.Path]::GetFileNameWithoutExtension($FullPath))
+ {
+ # No filename supplied.
+ $naerParams = @{
+ Exception = [System.ArgumentException]::new("Specified path [$FullPath] is a directory and not a file.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidArgument
+ ErrorId = 'ShortcutPathInvalid'
+ TargetObject = $FullPath
+ RecommendedAction = "Please confirm the provided value and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ }
+ elseif (!(& $Script:CommandTable.'Test-Path' -LiteralPath $PathDirectory -PathType Container))
+ {
+ try
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating shortcut directory [$PathDirectory]."
+ $null = & $Script:CommandTable.'New-Item' -Path $PathDirectory -ItemType Directory -Force
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to create shortcut directory [$PathDirectory].`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 3
+ throw
+ }
+ }
+
+ # Remove any pre-existing shortcut first.
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath $FullPath -PathType Leaf)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The shortcut [$FullPath] already exists. Deleting the file..."
+ & $Script:CommandTable.'Remove-ADTFile' -LiteralPath $FullPath
+ }
+
+ # Build out the shortcut.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating shortcut [$FullPath]."
+ if ([System.IO.Path]::GetExtension($Path) -eq '.url')
+ {
+ [String[]]$URLFile = '[InternetShortcut]', "URL=$TargetPath"
+ if ($PSBoundParameters.ContainsKey('IconIndex'))
+ {
+ $URLFile += "IconIndex=$IconIndex"
+ }
+ if ($IconLocation)
+ {
+ $URLFile += "IconFile=$IconLocation"
+ }
+ [System.IO.File]::WriteAllLines($FullPath, $URLFile, [System.Text.UTF8Encoding]::new($false))
+ }
+ else
+ {
+ $shortcut = [System.Activator]::CreateInstance([System.Type]::GetTypeFromProgID('WScript.Shell')).CreateShortcut($FullPath)
+ $shortcut.TargetPath = $TargetPath
+ if ($Arguments)
+ {
+ $shortcut.Arguments = $Arguments
+ }
+ if ($Description)
+ {
+ $shortcut.Description = $Description
+ }
+ if ($WorkingDirectory)
+ {
+ $shortcut.WorkingDirectory = $WorkingDirectory
+ }
+ if ($Hotkey)
+ {
+ $shortcut.Hotkey = $Hotkey
+ }
+ if ($IconLocation)
+ {
+ $shortcut.IconLocation = $IconLocation + ",$IconIndex"
+ }
+ $shortcut.WindowStyle = switch ($WindowStyle)
+ {
+ Normal { 1; break }
+ Maximized { 3; break }
+ Minimized { 7; break }
+ }
+
+ # Save the changes.
+ $shortcut.Save()
+
+ # Set shortcut to run program as administrator.
+ if ($RunAsAdmin)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Setting shortcut to run program as administrator.'
+ $fileBytes = [System.IO.FIle]::ReadAllBytes($FullPath)
+ $fileBytes[21] = $filebytes[21] -bor 32
+ [System.IO.FIle]::WriteAllBytes($FullPath, $fileBytes)
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to create shortcut [$Path]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTTemplate
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTTemplate
+{
+ <#
+ .SYNOPSIS
+ Creates a new folder containing a template front end and module folder, ready to customise.
+
+ .DESCRIPTION
+ Specify a destination path where a new folder will be created. You also have the option of creating a template for v3 compatibility mode.
+
+ .PARAMETER Destination
+ Path where the new folder should be created. Default is the current working directory.
+
+ .PARAMETER Name
+ Name of the newly created folder. Default is PSAppDeployToolkit_Version.
+
+ .PARAMETER Version
+ Defaults to 4 for the standard v4 template. Use 3 for the v3 compatibility mode template.
+
+ .PARAMETER Show
+ Opens the newly created folder in Windows Explorer.
+
+ .PARAMETER Force
+ If the destination folder already exists, this switch will force the creation of the new folder.
+
+ .PARAMETER PassThru
+ Returns the newly created folder object.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ New-ADTTemplate -Destination 'C:\Temp' -Name 'PSAppDeployToolkitv4'
+
+ Creates a new v4 template named PSAppDeployToolkitv4 under C:\Temp.
+
+ .EXAMPLE
+ New-ADTTemplate -Destination 'C:\Temp' -Name 'PSAppDeployToolkitv3' -Version 3
+
+ Creates a new v3 compatibility mode template named PSAppDeployToolkitv3 under C:\Temp.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/New-ADTTemplate
+ #>
+
+ [CmdletBinding(SupportsShouldProcess = $false)]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Destination = $ExecutionContext.SessionState.Path.CurrentLocation.Path,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = "PSAppDeployToolkit_")]
+ [System.String]$Name = "$($MyInvocation.MyCommand.Module.Name)_$($MyInvocation.MyCommand.Module.Version)",
+
+ [Parameter(Mandatory = $false)]
+ [ValidateRange(3, 4)]
+ [System.Int32]$Version = 4,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Show,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Force,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ # Initialize the function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Resolve the path to handle setups like ".\", etc.
+ # We can't use things like a DirectoryInfo cast as .NET doesn't
+ # track when the current location in PowerShell has been changed.
+ if (($resolvedDest = & $Script:CommandTable.'Resolve-Path' -LiteralPath $Destination -ErrorAction Ignore))
+ {
+ $Destination = $resolvedDest.Path
+ }
+
+ # Set up remaining variables.
+ $moduleName = $MyInvocation.MyCommand.Module.Name
+ $templatePath = & $Script:CommandTable.'Join-Path' -Path $Destination -ChildPath $Name
+ $templateModulePath = if ($Version.Equals(3))
+ {
+ [System.IO.Path]::Combine($templatePath, 'AppDeployToolkit', $moduleName)
+ }
+ else
+ {
+ [System.IO.Path]::Combine($templatePath, $moduleName)
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If we're running a release module, ensure the psd1 files haven't been tampered with.
+ if (($badFiles = & $Script:CommandTable.'Test-ADTReleaseBuildFileValidity' -LiteralPath $Script:PSScriptRoot))
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("One or more files within this module have invalid digital signatures.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidData
+ ErrorId = 'ADTDataFileSignatureError'
+ TargetObject = $badFiles
+ RecommendedAction = "Please re-download $($MyInvocation.MyCommand.Module.Name) and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+
+ # Create directories.
+ if ([System.IO.Directory]::Exists($templatePath) -and [System.IO.Directory]::GetFileSystemEntries($templatePath))
+ {
+ if (!$Force)
+ {
+ $naerParams = @{
+ Exception = [System.IO.IOException]::new("Folders [$templatePath] already exists and is not empty.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'NonEmptySubfolderError'
+ TargetObject = $templatePath
+ RecommendedAction = "Please remove the existing folder, supply a new name, or add the -Force parameter and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $templatePath -Recurse -Force
+ }
+ $null = & $Script:CommandTable.'New-Item' -Path "$templatePath\Files" -ItemType Directory -Force
+ $null = & $Script:CommandTable.'New-Item' -Path "$templatePath\SupportFiles" -ItemType Directory -Force
+
+ # Add in some empty files to the Files/SupportFiles folders to stop GitHub upload-artifact from dropping the empty folders.
+ $null = & $Script:CommandTable.'New-Item' -Name 'Add Setup Files Here.txt' -Path "$templatePath\Files" -ItemType File -Force
+ $null = & $Script:CommandTable.'New-Item' -Name 'Add Supporting Files Here.txt' -Path "$templatePath\SupportFiles" -ItemType File -Force
+
+ # Copy in the frontend files and the config/assets/strings.
+ & $Script:CommandTable.'Copy-Item' -Path "$Script:PSScriptRoot\Frontend\v$Version\*" -Destination $templatePath -Recurse -Force
+ & $Script:CommandTable.'Copy-Item' -LiteralPath "$Script:PSScriptRoot\Assets" -Destination $templatePath -Recurse -Force
+ & $Script:CommandTable.'Copy-Item' -LiteralPath "$Script:PSScriptRoot\Config" -Destination $templatePath -Recurse -Force
+ & $Script:CommandTable.'Copy-Item' -LiteralPath "$Script:PSScriptRoot\Strings" -Destination $templatePath -Recurse -Force
+
+ # Remove any digital signatures from the ps*1 files.
+ & $Script:CommandTable.'Get-ChildItem' -Path "$templatePath\*.ps*1" -Recurse | & {
+ process
+ {
+ if (($sigLine = $(($fileLines = [System.IO.File]::ReadAllLines($_.FullName)) -match '^# SIG # Begin signature block$')))
+ {
+ [System.IO.File]::WriteAllLines($_.FullName, $fileLines[0..($fileLines.IndexOf($sigLine) - 2)])
+ }
+ }
+ }
+
+ # Copy in the module files.
+ $null = & $Script:CommandTable.'New-Item' -Path $templateModulePath -ItemType Directory -Force
+ & $Script:CommandTable.'Copy-Item' -Path "$Script:PSScriptRoot\*" -Destination $templateModulePath -Recurse -Force
+
+ # Make the shipped module and its files read-only.
+ $(& $Script:CommandTable.'Get-Item' -LiteralPath $templateModulePath; & $Script:CommandTable.'Get-ChildItem' -LiteralPath $templateModulePath -Recurse) | & {
+ process
+ {
+ $_.Attributes = 'ReadOnly'
+ }
+ }
+
+ # Process the generated script to ensure the Import-Module is correct.
+ if ($Version.Equals(4))
+ {
+ $params = @{
+ LiteralPath = "$templatePath\Invoke-AppDeployToolkit.ps1"
+ Encoding = if ($PSVersionTable.PSEdition.Equals('Core')) { 'utf8BOM' } else { 'utf8' }
+ }
+ & $Script:CommandTable.'Out-File' -InputObject (& $Script:CommandTable.'Get-Content' @params -Raw).Replace('..\..\..\', $null) @params -Width ([System.Int32]::MaxValue) -Force
+ }
+
+ # Display the newly created folder in Windows Explorer.
+ if ($Show)
+ {
+ & ([System.IO.Path]::Combine([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Windows), 'explorer.exe')) $templatePath
+ }
+
+ # Return a DirectoryInfo object if passing through.
+ if ($PassThru)
+ {
+ return (& $Script:CommandTable.'Get-Item' -LiteralPath $templatePath)
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTValidateScriptErrorRecord
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTValidateScriptErrorRecord
+{
+ <#
+ .SYNOPSIS
+ Creates a new ErrorRecord for script validation errors.
+
+ .DESCRIPTION
+ This function creates a new ErrorRecord object for script validation errors. It takes the parameter name, provided value, exception message, and an optional inner exception to build a detailed error record. This helps in identifying and handling invalid parameter values in scripts.
+
+ .PARAMETER ParameterName
+ The name of the parameter that caused the validation error.
+
+ .PARAMETER ProvidedValue
+ The value provided for the parameter that caused the validation error.
+
+ .PARAMETER ExceptionMessage
+ The message describing the validation error.
+
+ .PARAMETER InnerException
+ An optional inner exception that provides more details about the validation error.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Management.Automation.ErrorRecord
+
+ This function returns an ErrorRecord object.
+
+ .EXAMPLE
+ PS C:\>$paramName = "FilePath"
+ PS C:\>$providedValue = "C:\InvalidPath"
+ PS C:\>$exceptionMessage = "The specified path does not exist."
+ PS C:\>New-ADTValidateScriptErrorRecord -ParameterName $paramName -ProvidedValue $providedValue -ExceptionMessage $exceptionMessage
+
+ Creates a new ErrorRecord for a validation error with the specified parameters.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/New-ADTValidateScriptErrorRecord
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = "This function does not change system state.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ParameterName,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [System.Object]$ProvidedValue,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ExceptionMessage,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Exception]$InnerException
+ )
+
+ # Build out new ErrorRecord and return it.
+ $naerParams = @{
+ Exception = if ($InnerException)
+ {
+ [System.ArgumentException]::new($ExceptionMessage, $ParameterName, $InnerException)
+ }
+ else
+ {
+ [System.ArgumentException]::new($ExceptionMessage, $ParameterName)
+ }
+ Category = [System.Management.Automation.ErrorCategory]::InvalidArgument
+ ErrorId = "Invalid$($ParameterName)ParameterValue"
+ TargetObject = $ProvidedValue
+ TargetName = $ProvidedValue
+ TargetType = $(if ($null -ne $ProvidedValue) { $ProvidedValue.GetType().Name })
+ RecommendedAction = "Review the supplied $($ParameterName) parameter value and try again."
+ }
+ return (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTZipFile
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTZipFile
+{
+ <#
+ .SYNOPSIS
+ Create a new zip archive or add content to an existing archive.
+
+ .DESCRIPTION
+ Create a new zip archive or add content to an existing archive by using PowerShell's Compress-Archive.
+
+ .PARAMETER Path
+ One or more paths to compress. Supports wildcards.
+
+ .PARAMETER LiteralPath
+ One or more literal paths to compress.
+
+ .PARAMETER DestinationPath
+ The file path for where the zip file should be created.
+
+ .PARAMETER CompressionLevel
+ The level of compression to apply to the zip file.
+
+ .PARAMETER Update
+ Specifies whether to update an existing zip file or not.
+
+ .PARAMETER Force
+ Specifies whether an existing zip file should be overwritten.
+
+ .PARAMETER RemoveSourceAfterArchiving
+ Remove the source path after successfully archiving the content.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ New-ADTZipFile -SourceDirectory 'E:\Testing\Logs' -DestinationPath 'E:\Testing\TestingLogs.zip'
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/New-ADTZipFile
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'LiteralPath')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$LiteralPath,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Path')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'LiteralPath')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DestinationPath,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Path')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'LiteralPath')]
+ [ValidateSet('Fastest', 'NoCompression', 'Optimal')]
+ [System.String]$CompressionLevel,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Path')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'LiteralPath')]
+ [System.Management.Automation.SwitchParameter]$Update,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Path')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'LiteralPath')]
+ [System.Management.Automation.SwitchParameter]$Force,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Path')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'LiteralPath')]
+ [System.Management.Automation.SwitchParameter]$RemoveSourceAfterArchiving
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Remove invalid characters from the supplied filename.
+ if (($DestinationArchiveFileName = & $Script:CommandTable.'Remove-ADTInvalidFileNameChars' -Name $DestinationArchiveFileName).Length -eq 0)
+ {
+ $naerParams = @{
+ Exception = [System.ArgumentException]::new('Invalid filename characters replacement resulted into an empty string.', $_)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidArgument
+ ErrorId = 'DestinationArchiveFileNameInvalid'
+ TargetObject = $DestinationArchiveFileName
+ RecommendedAction = "Please review the supplied value to '-DestinationArchiveFileName' and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+
+ # Remove parameters from PSBoundParameters that don't apply to Compress-Archive.
+ if ($PSBoundParameters.ContainsKey('RemoveSourceAfterArchiving'))
+ {
+ $null = $PSBoundParameters.Remove('RemoveSourceAfterArchiving')
+ }
+
+ # Get the specified source variable.
+ $sourcePath = & $Script:CommandTable.'Get-Variable' -Name $PSCmdlet.ParameterSetName -ValueOnly
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Get the full destination path where the archive will be stored.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating a zip archive with the requested content at destination path [$DestinationPath]."
+
+ # If the destination archive already exists, delete it if the -OverwriteArchive option was selected.
+ if ([System.IO.File]::Exists($DestinationPath) -and $OverwriteArchive)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "An archive at the destination path already exists, deleting file [$DestinationPath]."
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $DestinationPath -Force
+ }
+
+ # Create the archive file.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Compressing [$sourcePath] to destination path [$DestinationPath]..."
+ & $Script:CommandTable.'Compress-Archive' @PSBoundParameters
+
+ # If option was selected, recursively delete the source directory after successfully archiving the contents.
+ if ($RemoveSourceAfterArchiving)
+ {
+ try
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Recursively deleting [$sourcePath] as contents have been successfully archived."
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $Directory -Recurse -Force
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to recursively delete [$sourcePath].`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 2
+ }
+ }
+
+ # If the archive was created in session 0 or by an Admin, then it may only be readable by elevated users.
+ # Apply the parent folder's permissions to the archive file to fix the problem.
+ $parentPath = [System.IO.Path]::GetDirectoryName($DestinationPath)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "If the archive was created in session 0 or by an Admin, then it may only be readable by elevated users. Apply permissions from parent folder [$parentPath] to file [$DestinationPath]."
+ try
+ {
+ & $Script:CommandTable.'Set-Acl' -LiteralPath $DestinationPath -AclObject (& $Script:CommandTable.'Get-Acl' -Path $parentPath)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to apply parent folder's [$parentPath] permissions to file [$DestinationPath].`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 2
+ }
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to archive the requested file(s)."
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Open-ADTSession
+#
+#-----------------------------------------------------------------------------
+
+function Open-ADTSession
+{
+ <#
+ .SYNOPSIS
+ Opens a new ADT session.
+
+ .DESCRIPTION
+ This function initializes and opens a new ADT session with the specified parameters. It handles the setup of the session environment and processes any callbacks defined for the session. If the session fails to open, it handles the error and closes the session if necessary.
+
+ .PARAMETER SessionState
+ Caller's SessionState.
+
+ .PARAMETER DeploymentType
+ Specifies the type of deployment: Install, Uninstall, or Repair.
+
+ .PARAMETER DeployMode
+ Specifies the deployment mode: Interactive, NonInteractive, or Silent.
+
+ .PARAMETER AllowRebootPassThru
+ Allows reboot pass-through.
+
+ .PARAMETER TerminalServerMode
+ Enables Terminal Server mode.
+
+ .PARAMETER DisableLogging
+ Disables logging for the session.
+
+ .PARAMETER AppVendor
+ Specifies the application vendor.
+
+ .PARAMETER AppName
+ Specifies the application name.
+
+ .PARAMETER AppVersion
+ Specifies the application version.
+
+ .PARAMETER AppArch
+ Specifies the application architecture.
+
+ .PARAMETER AppLang
+ Specifies the application language.
+
+ .PARAMETER AppRevision
+ Specifies the application revision.
+
+ .PARAMETER AppSuccessExitCodes
+ Specifies the application exit codes.
+
+ .PARAMETER AppRebootExitCodes
+ Specifies the application reboot codes.
+
+ .PARAMETER AppScriptVersion
+ Specifies the application script version.
+
+ .PARAMETER AppScriptDate
+ Specifies the application script date.
+
+ .PARAMETER AppScriptAuthor
+ Specifies the application script author.
+
+ .PARAMETER InstallName
+ Specifies the install name.
+
+ .PARAMETER InstallTitle
+ Specifies the install title.
+
+ .PARAMETER DeployAppScriptFriendlyName
+ Specifies the friendly name of the deploy application script.
+
+ .PARAMETER DeployAppScriptVersion
+ Specifies the version of the deploy application script.
+
+ .PARAMETER DeployAppScriptDate
+ Specifies the date of the deploy application script.
+
+ .PARAMETER DeployAppScriptParameters
+ Specifies the parameters for the deploy application script.
+
+ .PARAMETER ScriptDirectory
+ Specifies the base path for Files and SupportFiles.
+
+ .PARAMETER DirFiles
+ Specifies the override path to Files.
+
+ .PARAMETER DirSupportFiles
+ Specifies the override path to SupportFiles.
+
+ .PARAMETER DefaultMsiFile
+ Specifies the default MSI file.
+
+ .PARAMETER DefaultMstFile
+ Specifies the default MST file.
+
+ .PARAMETER DefaultMspFiles
+ Specifies the default MSP files.
+
+ .PARAMETER DisableDefaultMsiProcessList
+ Specifies that the zero-config MSI code should not gather process names from the MSI file.
+
+ .PARAMETER LogName
+ Specifies an override for the default-generated log file name.
+
+ .PARAMETER SessionClass
+ Specifies an override for PSADT.Module.DeploymentSession class. Use this if you're deriving a class inheriting off PSAppDeployToolkit's base.
+
+ .PARAMETER ForceWimDetection
+ Specifies that WIM files should be detected and mounted during session initialization, irrespective of whether any App values are provided.
+
+ .PARAMETER PassThru
+ Passes the session object through the pipeline.
+
+ .PARAMETER UnboundArguments
+ Captures any additional arguments passed to the function.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ ADTSession
+
+ This function returns the session object if -PassThru is specified.
+
+ .EXAMPLE
+ Open-ADTSession -SessionState $ExecutionContext.SessionState -DeploymentType "Install" -DeployMode "Interactive"
+
+ Opens a new ADT session with the specified parameters.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Open-ADTSession
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SessionState]$SessionState,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Parameter')]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Module.DeploymentType]$DeploymentType,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Parameter')]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Module.DeployMode]$DeployMode,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Parameter')]
+ [System.Management.Automation.SwitchParameter]$AllowRebootPassThru,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Parameter')]
+ [System.Management.Automation.SwitchParameter]$TerminalServerMode,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Parameter')]
+ [System.Management.Automation.SwitchParameter]$DisableLogging,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$AppVendor,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$AppName,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$AppVersion,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$AppArch,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$AppLang,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$AppRevision,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [ValidateNotNullOrEmpty()]
+ [System.Version]$AppScriptVersion,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [ValidateNotNullOrEmpty()]
+ [System.DateTime]$AppScriptDate,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$AppScriptAuthor,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$InstallName,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyString()]
+ [System.String]$InstallTitle,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DeployAppScriptFriendlyName,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [ValidateNotNullOrEmpty()]
+ [System.Version]$DeployAppScriptVersion,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [ValidateNotNullOrEmpty()]
+ [System.DateTime]$DeployAppScriptDate,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Frontend Variable')]
+ [AllowEmptyCollection()]
+ [System.Collections.Generic.Dictionary[System.String, System.Object]]$DeployAppScriptParameters,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32[]]$AppSuccessExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32[]]$AppRebootExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ScriptDirectory -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (![System.IO.Directory]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName ScriptDirectory -ProvidedValue $_ -ExceptionMessage 'The specified directory does not exist.'))
+ }
+ return $_
+ })]
+ [System.String[]]$ScriptDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName DirFiles -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (![System.IO.Directory]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName DirFiles -ProvidedValue $_ -ExceptionMessage 'The specified directory does not exist.'))
+ }
+ return $_
+ })]
+ [System.String]$DirFiles,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName DirSupportFiles -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (![System.IO.Directory]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName DirSupportFiles -ProvidedValue $_ -ExceptionMessage 'The specified directory does not exist.'))
+ }
+ return $_
+ })]
+ [System.String]$DirSupportFiles,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DefaultMsiFile,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DefaultMstFile,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$DefaultMspFiles,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DisableDefaultMsiProcessList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName LogName -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if ([System.IO.Path]::GetExtension($_) -ne '.log')
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName LogName -ProvidedValue $_ -ExceptionMessage 'The specified name does not have a [.log] extension.'))
+ }
+ return $_
+ })]
+ [System.String]$LogName,
+
+ [Parameter(Mandatory = $false, DontShow = $true)]
+ [ValidateScript({
+ if ($null -eq $_)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName SessionClass -ProvidedValue $_ -ExceptionMessage 'The specified input is null or empty.'))
+ }
+ if (!$_.BaseType.Equals([PSADT.Module.DeploymentSession]))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName SessionClass -ProvidedValue $_ -ExceptionMessage 'The specified type is not derived from the DeploymentSession base class.'))
+ }
+ return $_
+ })]
+ [System.Type]$SessionClass = [PSADT.Module.DeploymentSession],
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ForceWimDetection,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false, ValueFromRemainingArguments = $true, DontShow = $true)]
+ [AllowNull()][AllowEmptyCollection()]
+ [System.Collections.Generic.List[System.Object]]$UnboundArguments
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $adtSession = $null
+ $errRecord = $null
+
+ # Determine whether this session is to be in compatibility mode.
+ $compatibilityMode = & $Script:CommandTable.'Test-ADTNonNativeCaller'
+ $callerInvocation = (& $Script:CommandTable.'Get-PSCallStack')[1].InvocationInfo
+ $noExitOnClose = !$callerInvocation.MyCommand.CommandType.Equals([System.Management.Automation.CommandTypes]::ExternalScript) -and !([System.Environment]::GetCommandLineArgs() -eq '-NonInteractive')
+
+ # Set up the ScriptDirectory if one wasn't provided.
+ if (!$PSBoundParameters.ContainsKey('ScriptDirectory'))
+ {
+ [System.String[]]$PSBoundParameters.ScriptDirectory = if (![System.String]::IsNullOrWhiteSpace(($scriptRoot = $SessionState.PSVariable.GetValue('PSScriptRoot', $null))))
+ {
+ if ($compatibilityMode)
+ {
+ [System.IO.Directory]::GetParent($scriptRoot).FullName
+ }
+ else
+ {
+ $scriptRoot
+ }
+ }
+ else
+ {
+ $ExecutionContext.SessionState.Path.CurrentLocation.Path
+ }
+ }
+
+ # Add any unbound arguments into $PSBoundParameters when using a derived class.
+ if ($PSBoundParameters.ContainsKey('UnboundArguments') -and !$SessionClass.Equals([PSADT.Module.DeploymentSession]))
+ {
+ $null = (& $Script:CommandTable.'Convert-ADTValuesFromRemainingArguments' -RemainingArguments $UnboundArguments).GetEnumerator().ForEach({
+ $PSBoundParameters.Add($_.Key, $_.Value)
+ })
+ }
+
+ # Remove any values from $PSBoundParameters that are null (empty strings, mostly).
+ $null = ($PSBoundParameters.GetEnumerator().Where({ [System.String]::IsNullOrWhiteSpace((& $Script:CommandTable.'Out-String' -InputObject $_.Value)) })).ForEach({ $PSBoundParameters.Remove($_.Key) })
+ }
+
+ process
+ {
+ # If this function is being called from the console or by AppDeployToolkitMain.ps1, clear all previous sessions and go for full re-initialization.
+ if (([System.String]::IsNullOrWhiteSpace($callerInvocation.InvocationName) -and [System.String]::IsNullOrWhiteSpace($callerInvocation.Line)) -or $compatibilityMode)
+ {
+ $Script:ADT.Sessions.Clear()
+ $Script:ADT.Initialized = $false
+ }
+ $firstSession = !$Script:ADT.Sessions.Count
+
+ # Commence the opening process.
+ try
+ {
+ try
+ {
+ # Initialize the module before opening the first session.
+ if ($firstSession -and !$Script:ADT.Initialized)
+ {
+ & $Script:CommandTable.'Initialize-ADTModule' -ScriptDirectory $PSBoundParameters.ScriptDirectory
+ }
+
+ # Instantiate the new session. The constructor will handle adding the session to the module's list.
+ $Script:ADT.Sessions.Add(($adtSession = $SessionClass::new($PSBoundParameters, $noExitOnClose, $(if ($compatibilityMode) { $SessionState }))))
+
+ # Invoke all callbacks.
+ foreach ($callback in $(if ($firstSession) { $Script:ADT.Callbacks.Starting }; $Script:ADT.Callbacks.Opening))
+ {
+ & $callback
+ }
+
+ # Add any unbound arguments into the $adtSession object as PSNoteProperty objects.
+ if ($PSBoundParameters.ContainsKey('UnboundArguments') -and $SessionClass.Equals([PSADT.Module.DeploymentSession]))
+ {
+ (& $Script:CommandTable.'Convert-ADTValuesFromRemainingArguments' -RemainingArguments $UnboundArguments).GetEnumerator() | & {
+ begin
+ {
+ $adtSessionProps = $adtSession.PSObject.Properties
+ }
+
+ process
+ {
+ $adtSessionProps.Add([System.Management.Automation.PSNoteProperty]::new($_.Key, $_.Value))
+ }
+ }
+ }
+
+ # Export the environment table to variables within the caller's scope.
+ if ($firstSession)
+ {
+ & $Script:CommandTable.'Export-ADTEnvironmentTableToSessionState' -SessionState $SessionState
+ }
+
+ # Change the install phase since we've finished initialising. This should get overwritten shortly.
+ $adtSession.InstallPhase = 'Execution'
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord ($errRecord = $_) -LogMessage "Failure occurred while opening new deployment session."
+ }
+ finally
+ {
+ # Terminate early if we have an active session that failed to open properly.
+ if ($errRecord)
+ {
+ if (!$adtSession)
+ {
+ & $Script:CommandTable.'Exit-ADTInvocation' -ExitCode 60008 -NoShellExit:$noExitOnClose
+ }
+ else
+ {
+ & $Script:CommandTable.'Close-ADTSession' -ExitCode 60008
+ }
+ }
+ }
+
+ # Return the most recent session if passing through.
+ if ($PassThru)
+ {
+ return $adtSession
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Out-ADTPowerShellEncodedCommand
+#
+#-----------------------------------------------------------------------------
+
+function Out-ADTPowerShellEncodedCommand
+{
+ <#
+ .SYNOPSIS
+ Encodes a PowerShell command into a Base64 string.
+
+ .DESCRIPTION
+ This function takes a PowerShell command as input and encodes it into a Base64 string. This is useful for passing commands to PowerShell through mechanisms that require encoded input.
+
+ .PARAMETER Command
+ The PowerShell command to be encoded.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ This function returns the encoded Base64 string representation of the input command.
+
+ .EXAMPLE
+ Out-ADTPowerShellEncodedCommand -Command 'Get-Process'
+
+ Encodes the "Get-Process" command into a Base64 string.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Out-ADTPowerShellEncodedCommand
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Command
+ )
+
+ return [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($Command))
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Register-ADTDll
+#
+#-----------------------------------------------------------------------------
+
+function Register-ADTDll
+{
+ <#
+ .SYNOPSIS
+ Register a DLL file.
+
+ .DESCRIPTION
+ This function registers a DLL file using regsvr32.exe. It ensures that the specified DLL file exists before attempting to register it. If the file does not exist, it throws an error.
+
+ .PARAMETER FilePath
+ Path to the DLL file.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return objects.
+
+ .EXAMPLE
+ Register-ADTDll -FilePath "C:\Test\DcTLSFileToDMSComp.dll"
+
+ Registers the specified DLL file.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Register-ADTDll
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTRegSvr32' @PSBoundParameters -Action Register
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTContentFromCache
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTContentFromCache
+{
+ <#
+ .SYNOPSIS
+ Removes the toolkit content from the cache folder on the local machine and reverts the $adtSession.DirFiles and $adtSession.SupportFiles directory.
+
+ .DESCRIPTION
+ This function removes the toolkit content from the cache folder on the local machine. It also reverts the $adtSession.DirFiles and $adtSession.SupportFiles directory to their original state. If the specified cache folder does not exist, it logs a message and exits.
+
+ .PARAMETER Path
+ The path to the software cache folder.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return objects.
+
+ .EXAMPLE
+ Remove-ADTContentFromCache -Path "$envWinDir\Temp\PSAppDeployToolkit"
+
+ Removes the toolkit content from the specified cache folder.
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTContentFromCache
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Path = "$((& $Script:CommandTable.'Get-ADTConfig').Toolkit.CachePath)\$((& $Script:CommandTable.'Get-ADTSession').InstallName)"
+ )
+
+ begin
+ {
+ try
+ {
+ $adtSession = & $Script:CommandTable.'Get-ADTSession'
+ $scriptDir = & $Script:CommandTable.'Get-ADTSessionCacheScriptDirectory'
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ if (![System.IO.Directory]::Exists($Path))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Cache folder [$Path] does not exist."
+ return
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing cache folder [$Path]."
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Remove-Item' -Path $Path -Recurse -Force
+ $adtSession.DirFiles = [System.IO.Path]::Combine($scriptDir, 'Files')
+ $adtSession.DirSupportFiles = [System.IO.Path]::Combine($scriptDir, 'SupportFiles')
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to remove cache folder [$Path]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTEdgeExtension
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTEdgeExtension
+{
+ <#
+ .SYNOPSIS
+ Removes an extension for Microsoft Edge using the ExtensionSettings policy.
+
+ .DESCRIPTION
+ This function removes an extension for Microsoft Edge using the ExtensionSettings policy: https://learn.microsoft.com/en-us/deployedge/microsoft-edge-manage-extensions-ref-guide.
+
+ This enables Edge Extensions to be installed and managed like applications, enabling extensions to be pushed to specific devices or users alongside existing GPO/Intune extension policies.
+
+ This should not be used in conjunction with Edge Management Service which leverages the same registry key to configure Edge extensions.
+
+ .PARAMETER ExtensionID
+ The ID of the extension to remove.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return objects.
+
+ .EXAMPLE
+ Remove-ADTEdgeExtension -ExtensionID "extensionID"
+
+ Removes the specified extension from Microsoft Edge.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ This function is provided as a template to remove an extension for Microsoft Edge. This should not be used in conjunction with Edge Management Service which leverages the same registry key to configure Edge extensions.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTEdgeExtension
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ExtensionID
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing extension with ID [$ExtensionID]."
+ try
+ {
+ try
+ {
+ # Return early if the extension isn't installed.
+ if (!($installedExtensions = & $Script:CommandTable.'Get-ADTEdgeExtensions').PSObject.Properties -or ($installedExtensions.PSObject.Properties.Name -notcontains $ExtensionID))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Extension with ID [$ExtensionID] is not configured. Removal not required."
+ return
+ }
+
+ # If the deploymentmode is Remove, remove the extension from the list.
+ $installedExtensions.PSObject.Properties.Remove($ExtensionID)
+ $null = & $Script:CommandTable.'Set-ADTRegistryKey' -Key Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge -Name ExtensionSettings -Value ($installedExtensions | & $Script:CommandTable.'ConvertTo-Json' -Compress)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTFile
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTFile
+{
+ <#
+ .SYNOPSIS
+ Removes one or more items from a given path on the filesystem.
+
+ .DESCRIPTION
+ This function removes one or more items from a given path on the filesystem. It can handle both wildcard paths and literal paths. If the specified path does not exist, it logs a warning instead of throwing an error. The function can also delete items recursively if the Recurse parameter is specified.
+
+ .PARAMETER Path
+ Specifies the path on the filesystem to be resolved. The value of Path will accept wildcards. Will accept an array of values.
+
+ .PARAMETER LiteralPath
+ Specifies the path on the filesystem to be resolved. The value of LiteralPath is used exactly as it is typed; no characters are interpreted as wildcards. Will accept an array of values.
+
+ .PARAMETER Recurse
+ Deletes the files in the specified location(s) and in all child items of the location(s).
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTFile -Path 'C:\Windows\Downloaded Program Files\Temp.inf'
+
+ Removes the specified file.
+
+ .EXAMPLE
+ Remove-ADTFile -LiteralPath 'C:\Windows\Downloaded Program Files' -Recurse
+
+ Removes the specified folder and all its contents recursively.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ This function continues on received errors by default. To have the function stop on an error, please provide `-ErrorAction Stop` on the end of your call.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTFile
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'LiteralPath', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Path', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'LiteralPath')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$LiteralPath,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ foreach ($Item in $PSBoundParameters[$PSCmdlet.ParameterSetName])
+ {
+ # Resolve the specified path, if the path does not exist, display a warning instead of an error.
+ try
+ {
+ try
+ {
+ $Item = if ($PSCmdlet.ParameterSetName -eq 'Path')
+ {
+ (& $Script:CommandTable.'Resolve-Path' -Path $Item).Path
+ }
+ else
+ {
+ (& $Script:CommandTable.'Resolve-Path' -LiteralPath $Item).Path
+ }
+ }
+ catch [System.Management.Automation.ItemNotFoundException]
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Unable to resolve the path [$Item] because it does not exist." -Severity 2
+ continue
+ }
+ catch [System.Management.Automation.DriveNotFoundException]
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Unable to resolve the path [$Item] because the drive does not exist." -Severity 2
+ continue
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to resolve the path for deletion [$Item]."
+ continue
+ }
+
+ # Delete specified path if it was successfully resolved.
+ try
+ {
+ try
+ {
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath $Item -PathType Container)
+ {
+ if (!$Recurse)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Skipping folder [$Item] because the Recurse switch was not specified."
+ continue
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Deleting file(s) recursively in path [$Item]..."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Deleting file in path [$Item]..."
+ }
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $Item -Recurse:$Recurse -Force
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to delete items in path [$Item]."
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTFileFromUserProfiles
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTFileFromUserProfiles
+{
+ <#
+ .SYNOPSIS
+ Removes one or more items from each user profile on the system.
+
+ .DESCRIPTION
+ This function removes one or more items from each user profile on the system. It can handle both wildcard paths and literal paths. If the specified path does not exist, it logs a warning instead of throwing an error. The function can also delete items recursively if the Recurse parameter is specified. Additionally, it allows excluding specific NT accounts, system profiles, service profiles, and the default user profile.
+
+ .PARAMETER Path
+ Specifies the path to append to the root of the user profile to be resolved. The value of Path will accept wildcards. Will accept an array of values.
+
+ .PARAMETER LiteralPath
+ Specifies the path to append to the root of the user profile to be resolved. The value of LiteralPath is used exactly as it is typed; no characters are interpreted as wildcards. Will accept an array of values.
+
+ .PARAMETER Recurse
+ Deletes the files in the specified location(s) and in all child items of the location(s).
+
+ .PARAMETER ExcludeNTAccount
+ Specify NT account names in Domain\Username format to exclude from the list of user profiles.
+
+ .PARAMETER ExcludeDefaultUser
+ Exclude the Default User.
+
+ .PARAMETER IncludeSystemProfiles
+ Include system profiles: SYSTEM, LOCAL SERVICE, NETWORK SERVICE.
+
+ .PARAMETER IncludeServiceProfiles
+ Include service profiles where NTAccount begins with NT SERVICE.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTFileFromUserProfiles -Path "AppData\Roaming\MyApp\config.txt"
+
+ Removes the specified file from each user profile on the system.
+
+ .EXAMPLE
+ Remove-ADTFileFromUserProfiles -Path "AppData\Local\MyApp" -Recurse
+
+ Removes the specified folder and all its contents recursively from each user profile on the system.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTFileFromUserProfiles
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'LiteralPath', Justification = "This parameter is accessed programmatically via the ParameterSet it's within, which PSScriptAnalyzer doesn't understand.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Path', Justification = "This parameter is accessed programmatically via the ParameterSet it's within, which PSScriptAnalyzer doesn't understand.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'Path')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Path,
+
+ [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'LiteralPath')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$LiteralPath,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ExcludeNTAccount,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeDefaultUser,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeSystemProfiles,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeServiceProfiles
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $RemoveFileSplat = @{
+ Recurse = $Recurse
+ }
+ $GetUserProfileSplat = @{
+ IncludeSystemProfiles = $IncludeSystemProfiles
+ IncludeServiceProfiles = $IncludeServiceProfiles
+ ExcludeDefaultUser = $ExcludeDefaultUser
+ }
+ if ($ExcludeNTAccount)
+ {
+ $GetUserProfileSplat.ExcludeNTAccount = $ExcludeNTAccount
+ }
+
+ # Store variable based on ParameterSetName.
+ $pathVar = & $Script:CommandTable.'Get-Variable' -Name $PSCmdlet.ParameterSetName
+ }
+
+ process
+ {
+ foreach ($UserProfilePath in (& $Script:CommandTable.'Get-ADTUserProfiles' @GetUserProfileSplat).ProfilePath)
+ {
+ $RemoveFileSplat.Path = $pathVar.Value | & { process { [System.IO.Path]::Combine($UserProfilePath, $_) } }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing $($pathVar.Name) [$($pathVar.Value)] from $UserProfilePath`:"
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Remove-ADTFile' @RemoveFileSplat
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTFolder
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTFolder
+{
+ <#
+ .SYNOPSIS
+ Remove folder and files if they exist.
+
+ .DESCRIPTION
+ This function removes a folder and all files within it, with or without recursion, in a given path. If the specified folder does not exist, it logs a warning instead of throwing an error. The function can also delete items recursively if the DisableRecursion parameter is not specified.
+
+ .PARAMETER Path
+ Path to the folder to remove.
+
+ .PARAMETER DisableRecursion
+ Disables recursion while deleting.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTFolder -Path "$envWinDir\Downloaded Program Files"
+
+ Deletes all files and subfolders in the Windows\Downloads Program Files folder.
+
+ .EXAMPLE
+ Remove-ADTFolder -Path "$envTemp\MyAppCache" -DisableRecursion
+
+ Deletes all files in the Temp\MyAppCache folder but does not delete any subfolders.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTFolder
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.IO.DirectoryInfo]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DisableRecursion
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ # Return early if the folder doesn't exist.
+ if (!($Path | & $Script:CommandTable.'Test-Path' -PathType Container))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Folder [$Path] does not exist."
+ return
+ }
+
+ try
+ {
+ try
+ {
+ # With -Recurse, we can just send it and return early.
+ if (!$DisableRecursion)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Deleting folder [$Path] recursively..."
+ & $Script:CommandTable.'Invoke-ADTCommandWithRetries' -Command $Script:CommandTable.'Remove-Item' -LiteralPath $Path -Force -Recurse
+ return
+ }
+
+ # Without recursion, we can only send it if the folder has no items as Remove-Item will ask for confirmation without recursion.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Deleting folder [$Path] without recursion..."
+ if (!($ListOfChildItems = & $Script:CommandTable.'Get-ChildItem' -LiteralPath $Path -Force))
+ {
+ & $Script:CommandTable.'Invoke-ADTCommandWithRetries' -Command $Script:CommandTable.'Remove-Item' -LiteralPath $Path -Force
+ return
+ }
+
+ # We must have some subfolders, let's see what we can do.
+ $SubfoldersSkipped = foreach ($item in $ListOfChildItems)
+ {
+ # Check whether this item is a folder
+ if ($item -is [System.IO.DirectoryInfo])
+ {
+ # Item is a folder. Check if its empty.
+ if (($item | & $Script:CommandTable.'Get-ChildItem' -Force | & $Script:CommandTable.'Measure-Object').Count -eq 0)
+ {
+ # The folder is empty, delete it
+ & $Script:CommandTable.'Invoke-ADTCommandWithRetries' -Command $Script:CommandTable.'Remove-Item' -LiteralPath $item.FullName -Force
+ }
+ else
+ {
+ # Folder is not empty, skip it.
+ $item
+ }
+ }
+ else
+ {
+ # Item is a file. Delete it.
+ & $Script:CommandTable.'Invoke-ADTCommandWithRetries' -Command $Script:CommandTable.'Remove-Item' -LiteralPath $item.FullName -Force
+ }
+ }
+ if ($SubfoldersSkipped)
+ {
+ $naerParams = @{
+ Exception = [System.IO.IOException]::new("The following folders are not empty ['$($SubfoldersSkipped.FullName.Replace($Path.FullName, $null) -join "'; '")'].")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'NonEmptySubfolderError'
+ TargetObject = $SubfoldersSkipped
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to delete folder(s) and file(s) from path [$Path]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTInvalidFileNameChars
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTInvalidFileNameChars
+{
+ <#
+ .SYNOPSIS
+ Remove invalid characters from the supplied string.
+
+ .DESCRIPTION
+ This function removes invalid characters from the supplied string and returns a valid filename as a string. It ensures that the resulting string does not contain any characters that are not allowed in filenames. This function should not be used for entire paths as '\' is not a valid filename character.
+
+ .PARAMETER Name
+ Text to remove invalid filename characters from.
+
+ .INPUTS
+ System.String
+
+ A string containing invalid filename characters.
+
+ .OUTPUTS
+ System.String
+
+ Returns the input string with the invalid characters removed.
+
+ .EXAMPLE
+ Remove-ADTInvalidFileNameChars -Name "Filename/\1"
+
+ Removes invalid filename characters from the string "Filename/\1".
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ This function always returns a string; however, it can be empty if the name only contains invalid characters. Do not use this command for an entire path as '\' is not a valid filename character.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTInvalidFileNameChars
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [AllowEmptyString()]
+ [System.String]$Name
+ )
+
+ process
+ {
+ return ($Name.Trim() -replace "[$([System.Text.RegularExpressions.Regex]::Escape([System.String]::Join($null, [System.IO.Path]::GetInvalidFileNameChars())))]")
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTRegistryKey
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTRegistryKey
+{
+ <#
+ .SYNOPSIS
+ Deletes the specified registry key or value.
+
+ .DESCRIPTION
+ This function deletes the specified registry key or value. It can handle both registry keys and values, and it supports recursive deletion of registry keys. If the SID parameter is specified, it converts HKEY_CURRENT_USER registry keys to the HKEY_USERS\$SID format, allowing for the manipulation of HKCU registry settings for all users on the system.
+
+ .PARAMETER Key
+ Path of the registry key to delete.
+
+ .PARAMETER Name
+ Name of the registry value to delete.
+
+ .PARAMETER Recurse
+ Delete registry key recursively.
+
+ .PARAMETER SID
+ The security identifier (SID) for a user. Specifying this parameter will convert a HKEY_CURRENT_USER registry key to the HKEY_USERS\$SID format.
+
+ Specify this parameter from the Invoke-ADTAllUsersRegistryAction function to read/edit HKCU registry settings for all users on the system.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTRegistryKey -Key 'HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce'
+
+ Deletes the specified registry key.
+
+ .EXAMPLE
+ Remove-ADTRegistryKey -Key 'HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Run' -Name 'RunAppInstall'
+
+ Deletes the specified registry value.
+
+ .EXAMPLE
+ Remove-ADTRegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Example' -Name '(Default)'
+
+ Deletes the default registry value in the specified key.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTRegistryKey
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Recurse,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If the SID variable is specified, then convert all HKEY_CURRENT_USER key's to HKEY_USERS\$SID.
+ $Key = if ($PSBoundParameters.ContainsKey('SID'))
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key -SID $SID
+ }
+ else
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key
+ }
+
+ if (!$Name)
+ {
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $Key))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Unable to delete registry key [$Key] because it does not exist." -Severity 2
+ return
+ }
+
+ if ($Recurse)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Deleting registry key recursively [$Key]."
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $Key -Force -Recurse
+ }
+ elseif (!(& $Script:CommandTable.'Get-ChildItem' -LiteralPath $Key))
+ {
+ # Check if there are subkeys of $Key, if so, executing Remove-Item will hang. Avoiding this with Get-ChildItem.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Deleting registry key [$Key]."
+ $null = & $Script:CommandTable.'Remove-Item' -LiteralPath $Key -Force
+ }
+ else
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("Unable to delete child key(s) of [$Key] without [-Recurse] switch.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'SubKeyRecursionError'
+ TargetObject = $Key
+ RecommendedAction = "Please run this command again with [-Recurse]."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ }
+ else
+ {
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $Key))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Unable to delete registry value [$Key] [$Name] because registry key does not exist." -Severity 2
+ return
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Deleting registry value [$Key] [$Name]."
+ if ($Name -eq '(Default)')
+ {
+ # Remove (Default) registry key value with the following workaround because Remove-ItemProperty cannot remove the (Default) registry key value.
+ $null = (& $Script:CommandTable.'Get-Item' -LiteralPath $Key).OpenSubKey('', 'ReadWriteSubTree').DeleteValue('')
+ }
+ else
+ {
+ $null = & $Script:CommandTable.'Remove-ItemProperty' -LiteralPath $Key -Name $Name -Force
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch [System.Management.Automation.PSArgumentException]
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Unable to delete registry value [$Key] [$Name] because it does not exist." -Severity 2
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to delete registry $(("key [$Key]", "value [$Key] [$Name]")[!!$Name])."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTSessionClosingCallback
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTSessionClosingCallback
+{
+ <#
+ .SYNOPSIS
+ Removes a callback function from the ADT session closing event.
+
+ .DESCRIPTION
+ This function removes a specified callback function from the ADT session closing event. The callback function must be provided as a parameter. If the operation fails, it throws a terminating error.
+
+ .PARAMETER Callback
+ The callback function to remove from the ADT session closing event.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTSessionClosingCallback -Callback (Get-Command -Name 'MyCallbackFunction')
+
+ Removes the specified callback function from the ADT session closing event.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTSessionClosingCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Closing -Action Remove @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTSessionFinishingCallback
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTSessionFinishingCallback
+{
+ <#
+ .SYNOPSIS
+ Removes a callback function from the ADT session finishing event.
+
+ .DESCRIPTION
+ This function removes a specified callback function from the ADT session finishing event. The callback function must be provided as a parameter. If the operation fails, it throws a terminating error.
+
+ .PARAMETER Callback
+ The callback function to remove from the ADT session finishing event.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTSessionFinishingCallback -Callback (Get-Command -Name 'MyCallbackFunction')
+
+ Removes the specified callback function from the ADT session finishing event.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTSessionFinishingCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Finishing -Action Remove @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTSessionOpeningCallback
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTSessionOpeningCallback
+{
+ <#
+ .SYNOPSIS
+ Removes a callback function from the ADT session opening event.
+
+ .DESCRIPTION
+ This function removes a specified callback function from the ADT session opening event. The callback function must be provided as a parameter. If the operation fails, it throws a terminating error.
+
+ .PARAMETER Callback
+ The callback function to remove from the ADT session opening event.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTSessionOpeningCallback -Callback (Get-Command -Name 'MyCallbackFunction')
+
+ Removes the specified callback function from the ADT session opening event.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTSessionOpeningCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Opening -Action Remove @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Remove-ADTSessionStartingCallback
+#
+#-----------------------------------------------------------------------------
+
+function Remove-ADTSessionStartingCallback
+{
+ <#
+ .SYNOPSIS
+ Removes a callback function from the ADT session starting event.
+
+ .DESCRIPTION
+ This function removes a specified callback function from the ADT session starting event. The callback function must be provided as a parameter. If the operation fails, it throws a terminating error.
+
+ .PARAMETER Callback
+ The callback function to remove from the ADT session starting event.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Remove-ADTSessionStartingCallback -Callback (Get-Command -Name 'MyCallbackFunction')
+
+ Removes the specified callback function from the ADT session starting event.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Remove-ADTSessionStartingCallback
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.CommandInfo[]]$Callback
+ )
+
+ # Send it off to the backend function.
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTSessionCallbackOperation' -Type Starting -Action Remove @PSBoundParameters
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Reset-ADTDeferHistory
+#
+#-----------------------------------------------------------------------------
+
+function Reset-ADTDeferHistory
+{
+ <#
+ .SYNOPSIS
+ Reset the history of deferrals in the registry for the current application.
+
+ .DESCRIPTION
+ Reset the history of deferrals in the registry for the current application.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Reset-DeferHistory
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Reset-ADTDeferHistory
+
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ try
+ {
+ (& $Script:CommandTable.'Get-ADTSession').ResetDeferHistory()
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Resolve-ADTErrorRecord
+#
+#-----------------------------------------------------------------------------
+
+function Resolve-ADTErrorRecord
+{
+ <#
+ .SYNOPSIS
+ Enumerates ErrorRecord details.
+
+ .DESCRIPTION
+ Enumerates an ErrorRecord, or a collection of ErrorRecord properties. This function can filter and display specific properties of the ErrorRecord, and can exclude certain parts of the error details.
+
+ .PARAMETER ErrorRecord
+ The ErrorRecord to resolve. For usage in a catch block, you'd use the automatic variable `$PSItem`. For usage out of a catch block, you can access the global $Error array's first error (on index 0).
+
+ .PARAMETER Property
+ The list of properties to display from the ErrorRecord. Use "*" to display all properties.
+
+ Default list of error properties is: Message, FullyQualifiedErrorId, ScriptStackTrace, PositionMessage, InnerException
+
+ .PARAMETER ExcludeErrorRecord
+ Exclude ErrorRecord details as represented by $ErrorRecord.
+
+ .PARAMETER ExcludeErrorInvocation
+ Exclude ErrorRecord invocation information as represented by $ErrorRecord.InvocationInfo.
+
+ .PARAMETER ExcludeErrorException
+ Exclude ErrorRecord exception details as represented by $ErrorRecord.Exception.
+
+ .PARAMETER ExcludeErrorInnerException
+ Exclude ErrorRecord inner exception details as represented by $ErrorRecord.Exception.InnerException. Will retrieve all inner exceptions if there is more than one.
+
+ .INPUTS
+ System.Management.Automation.ErrorRecord
+
+ Accepts one or more ErrorRecord objects via the pipeline.
+
+ .OUTPUTS
+ System.String
+
+ Displays the ErrorRecord details.
+
+ .EXAMPLE
+ Resolve-ADTErrorRecord
+
+ Enumerates the details of the last ErrorRecord.
+
+ .EXAMPLE
+ Resolve-ADTErrorRecord -Property *
+
+ Enumerates all properties of the last ErrorRecord.
+
+ .EXAMPLE
+ Resolve-ADTErrorRecord -Property InnerException
+
+ Enumerates only the InnerException property of the last ErrorRecord.
+
+ .EXAMPLE
+ Resolve-ADTErrorRecord -ExcludeErrorInvocation
+
+ Enumerates the details of the last ErrorRecord, excluding the invocation information.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Resolve-ADTErrorRecord
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.String])]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ErrorRecord]$ErrorRecord,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [SupportsWildcards()]
+ [System.String[]]$Property = ('Message', 'InnerException', 'FullyQualifiedErrorId', 'ScriptStackTrace', 'PositionMessage'),
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeErrorRecord,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeErrorInvocation,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeErrorException,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$ExcludeErrorInnerException
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $propsIsWildCard = $($Property).Equals('*')
+
+ # Allows selecting and filtering the properties on the error object if they exist.
+ filter Get-ErrorPropertyNames
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$InputObject
+ )
+
+ # Store all properties.
+ $properties = $InputObject | & $Script:CommandTable.'Get-Member' -MemberType *Property | & $Script:CommandTable.'Select-Object' -ExpandProperty Name
+
+ # If we've asked for all properties, return early with the above.
+ if ($propsIsWildCard)
+ {
+ return $properties | & { process { if (![System.String]::IsNullOrWhiteSpace(($InputObject.$_ | & $Script:CommandTable.'Out-String').Trim())) { return $_ } } }
+ }
+
+ # Return all valid properties in the order used by the caller.
+ return $Property | & { process { if (($properties -contains $_) -and ![System.String]::IsNullOrWhiteSpace(($InputObject.$_ | & $Script:CommandTable.'Out-String').Trim())) { return $_ } } }
+ }
+ }
+
+ process
+ {
+ # Build out error objects to process in the right order.
+ $errorObjects = $(
+ $canDoException = !$ExcludeErrorException -and $ErrorRecord.Exception
+ if (!$propsIsWildCard -and $canDoException)
+ {
+ $ErrorRecord.Exception
+ }
+ if (!$ExcludeErrorRecord)
+ {
+ $ErrorRecord
+ }
+ if (!$ExcludeErrorInvocation -and $ErrorRecord.InvocationInfo)
+ {
+ $ErrorRecord.InvocationInfo
+ }
+ if ($propsIsWildCard -and $canDoException)
+ {
+ $ErrorRecord.Exception
+ }
+ )
+
+ # Open property collector and build it out.
+ $logErrorProperties = [ordered]@{}
+ foreach ($errorObject in $errorObjects)
+ {
+ # Store initial property count.
+ $propCount = $logErrorProperties.Count
+
+ # Add in all properties for the object.
+ foreach ($propName in ($errorObject | Get-ErrorPropertyNames))
+ {
+ $logErrorProperties.Add($propName, ($errorObject.$propName).ToString().Trim())
+ }
+
+ # Append a new line to the last value for formatting purposes.
+ if (!$propCount.Equals($logErrorProperties.Count))
+ {
+ $logErrorProperties.($logErrorProperties.Keys | & $Script:CommandTable.'Select-Object' -Last 1) += "`n"
+ }
+ }
+
+ # Build out error properties.
+ $logErrorMessage = [System.String]::Join("`n", "Error Record:", "-------------", $null, (& $Script:CommandTable.'Out-String' -InputObject (& $Script:CommandTable.'Format-List' -InputObject ([pscustomobject]$logErrorProperties)) -Width ([System.Int32]::MaxValue)).Trim())
+
+ # Capture Error Inner Exception(s).
+ if (!$ExcludeErrorInnerException -and $ErrorRecord.Exception -and $ErrorRecord.Exception.InnerException)
+ {
+ # Set up initial variables.
+ $innerExceptions = [System.Collections.Specialized.StringCollection]::new()
+ $errInnerException = $ErrorRecord.Exception.InnerException
+
+ # Get all inner exceptions.
+ while ($errInnerException)
+ {
+ # Add a divider if we've already added a record.
+ if ($innerExceptions.Count)
+ {
+ $null = $innerExceptions.Add("`n$('~' * 40)`n")
+ }
+
+ # Add error record and get next inner exception.
+ $null = $innerExceptions.Add(($errInnerException | & $Script:CommandTable.'Select-Object' -Property ($errInnerException | Get-ErrorPropertyNames) | & $Script:CommandTable.'Format-List' | & $Script:CommandTable.'Out-String' -Width ([System.Int32]::MaxValue)).Trim())
+ $errInnerException = $errInnerException.InnerException
+ }
+
+ # Output all inner exceptions to the caller.
+ $logErrorMessage += "`n`n`n$([System.String]::Join("`n", "Error Inner Exception(s):", "-------------------------", $null, ($innerExceptions -join "`n")))"
+ }
+
+ # Output the error message to the caller.
+ return $logErrorMessage
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Send-ADTKeys
+#
+#-----------------------------------------------------------------------------
+
+function Send-ADTKeys
+{
+ <#
+ .SYNOPSIS
+ Send a sequence of keys to one or more application windows.
+
+ .DESCRIPTION
+ Send a sequence of keys to one or more application windows. If the window title searched for returns more than one window, then all of them will receive the sent keys.
+
+ Function does not work in SYSTEM context unless launched with "psexec.exe -s -i" to run it as an interactive process under the SYSTEM account.
+
+ .PARAMETER WindowTitle
+ The title of the application window to search for using regex matching.
+
+ .PARAMETER GetAllWindowTitles
+ Get titles for all open windows on the system.
+
+ .PARAMETER WindowHandle
+ Send keys to a specific window where the Window Handle is already known.
+
+ .PARAMETER Keys
+ The sequence of keys to send. Info on Key input at: http://msdn.microsoft.com/en-us/library/System.Windows.Forms.SendKeys(v=vs.100).aspx
+
+ .PARAMETER WaitSeconds
+ This parameter is obsolete and will be removed in PSAppDeployToolkit 4.2.0. Please use `-WaitDuration` instead.
+
+ .PARAMETER WaitDuration
+ An optional amount of time to wait after the sending of the keys.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Send-ADTKeys -WindowTitle 'foobar - Notepad' -Keys 'Hello world'
+
+ Send the sequence of keys "Hello world" to the application titled "foobar - Notepad".
+
+ .EXAMPLE
+ Send-ADTKeys -WindowTitle 'foobar - Notepad' -Keys 'Hello world' WaitDuration (New-TimeSpan -Seconds 5)
+
+ Send the sequence of keys "Hello world" to the application titled "foobar - Notepad" and wait 5 seconds.
+
+ .EXAMPLE
+ Send-ADTKeys -WindowHandle ([IntPtr]17368294) -Keys 'Hello World'
+
+ Send the sequence of keys "Hello World" to the application with a Window Handle of '17368294'.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ http://msdn.microsoft.com/en-us/library/System.Windows.Forms.SendKeys(v=vs.100).aspx
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Send-ADTKeys
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'WindowTitle')]
+ [AllowEmptyString()]
+ [ValidateNotNull()]
+ [System.String]$WindowTitle,
+
+ [Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'GetAllWindowTitles')]
+ [System.Management.Automation.SwitchParameter]$GetAllWindowTitles,
+
+ [Parameter(Mandatory = $true, Position = 2, ParameterSetName = 'WindowHandle')]
+ [ValidateNotNullOrEmpty()]
+ [System.IntPtr]$WindowHandle,
+
+ [Parameter(Mandatory = $true, Position = 3, ParameterSetName = 'WindowTitle')]
+ [Parameter(Mandatory = $true, Position = 3, ParameterSetName = 'GetAllWindowTitles')]
+ [Parameter(Mandatory = $true, Position = 3, ParameterSetName = 'WindowHandle')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Keys,
+
+ [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'WindowTitle')]
+ [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'GetAllWindowTitles')]
+ [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'WindowHandle')]
+ [System.Obsolete("Please use 'WaitDuration' instead as this will be removed in PSAppDeployToolkit 4.2.0.")]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$WaitSeconds,
+
+ [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'WindowTitle')]
+ [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'GetAllWindowTitles')]
+ [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'WindowHandle')]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$WaitDuration
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ $gawtParams = @{ $PSCmdlet.ParameterSetName = & $Script:CommandTable.'Get-Variable' -Name $PSCmdlet.ParameterSetName -ValueOnly }
+
+ # Log the deprecation of -WaitSeconds to the log.
+ if ($PSBoundParameters.ContainsKey('WaitSeconds'))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The parameter [-WaitSeconds] is obsolete and will be removed in PSAppDeployToolkit 4.2.0. Please use [-WaitDuration] instead." -Severity 2
+ if (!$PSBoundParameters.ContainsKey('WaitDuration'))
+ {
+ $WaitDuration = [System.TimeSpan]::FromSeconds($WaitSeconds)
+ }
+ }
+ }
+
+ process
+ {
+ # Get the specified windows.
+ try
+ {
+ if (!($Windows = & $Script:CommandTable.'Get-ADTWindowTitle' @gawtParams))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "No windows matching the specified input were discovered." -Severity 2
+ return
+ }
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+
+ # Process each found window.
+ foreach ($window in $Windows)
+ {
+ try
+ {
+ try
+ {
+ # Bring the window to the foreground and make sure it's enabled.
+ if (![PSADT.GUI.UiAutomation]::BringWindowToFront($window.WindowHandle))
+ {
+ $naerParams = @{
+ Exception = [System.ApplicationException]::new('Failed to bring window to foreground.')
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'WindowHandleForegroundError'
+ TargetObject = $window
+ RecommendedAction = "Please check the status of this window and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ if (![PSADT.LibraryInterfaces.User32]::IsWindowEnabled($window.WindowHandle))
+ {
+ $naerParams = @{
+ Exception = [System.ApplicationException]::new('Unable to send keys to window because it may be disabled due to a modal dialog being shown.')
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'WindowHandleDisabledError'
+ TargetObject = $window
+ RecommendedAction = "Please check the status of this window and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Send the Key sequence.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Sending key(s) [$Keys] to window title [$($window.WindowTitle)] with window handle [$($window.WindowHandle)]."
+ [System.Windows.Forms.SendKeys]::SendWait($Keys)
+ if ($WaitDuration)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Sleeping for [$($WaitDuration.TotalSeconds)] seconds."
+ [System.Threading.Thread]::Sleep($WaitDuration)
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to send keys to window title [$($window.WindowTitle)] with window handle [$($window.WindowHandle)]." -ErrorAction SilentlyContinue
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTActiveSetup
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTActiveSetup
+{
+ <#
+ .SYNOPSIS
+ Creates an Active Setup entry in the registry to execute a file for each user upon login.
+
+ .DESCRIPTION
+ Active Setup allows handling of per-user changes registry/file changes upon login.
+
+ A registry key is created in the HKLM registry hive which gets replicated to the HKCU hive when a user logs in.
+
+ If the "Version" value of the Active Setup entry in HKLM is higher than the version value in HKCU, the file referenced in "StubPath" is executed.
+
+ This Function:
+
+ - Creates the registry entries in "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\$($adtSession.InstallName)".
+ - Creates StubPath value depending on the file extension of the $StubExePath parameter.
+ - Handles Version value with YYYYMMDDHHMMSS granularity to permit re-installs on the same day and still trigger Active Setup after Version increase.
+ - Copies/overwrites the StubPath file to $StubExePath destination path if file exists in 'Files' subdirectory of script directory.
+ - Executes the StubPath file for the current user based on $NoExecuteForCurrentUser (no need to logout/login to trigger Active Setup).
+
+ .PARAMETER StubExePath
+ Use this parameter to specify the destination path of the file that will be executed upon user login.
+
+ Note: Place the file you want users to execute in the '\Files' subdirectory of the script directory and the toolkit will install it to the path specificed in this parameter.
+
+ .PARAMETER Arguments
+ Arguments to pass to the file being executed.
+
+ .PARAMETER Wow6432Node
+ Specify this switch to use Active Setup entry under Wow6432Node on a 64-bit OS.
+
+ .PARAMETER ExecutionPolicy
+ Specifies the ExecutionPolicy to set when StubExePath is a PowerShell script..
+
+ .PARAMETER Version
+ Optional. Specify version for Active setup entry. Active Setup is not triggered if Version value has more than 8 consecutive digits. Use commas to get around this limitation. Default: YYYYMMDDHHMMSS
+
+ Note:
+ - Do not use this parameter if it is not necessary. PSADT will handle this parameter automatically using the time of the installation as the version number.
+ - In Windows 10, Scripts and EXEs might be blocked by AppLocker. Ensure that the path given to -StubExePath will permit end users to run Scripts and EXEs unelevated.
+
+ .PARAMETER Locale
+ Optional. Arbitrary string used to specify the installation language of the file being executed. Not replicated to HKCU.
+
+ .PARAMETER PurgeActiveSetupKey
+ Remove Active Setup entry from HKLM registry hive. Will also load each logon user's HKCU registry hive to remove Active Setup entry. Function returns after purging.
+
+ .PARAMETER DisableActiveSetup
+ Disables the Active Setup entry so that the StubPath file will not be executed. This also enables -NoExecuteForCurrentUser.
+
+ .PARAMETER NoExecuteForCurrentUser
+ Specifies whether the StubExePath should be executed for the current user. Since this user is already logged in, the user won't have the application started without logging out and logging back in.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if Active Setup entry was created or updated, $false if Active Setup entry was not created or updated.
+
+ .EXAMPLE
+ Set-ADTActiveSetup -StubExePath 'C:\Users\Public\Company\ProgramUserConfig.vbs' -Arguments '/Silent' -Description 'Program User Config' -Key 'ProgramUserConfig' -Locale 'en'
+
+ .EXAMPLE
+ Set-ADTActiveSetup -StubExePath "$envWinDir\regedit.exe" -Arguments "/S `"%SystemDrive%\Program Files (x86)\PS App Deploy\PSAppDeployHKCUSettings.reg`"" -Description 'PS App Deploy Config' -Key 'PS_App_Deploy_Config'
+
+ .EXAMPLE
+ Set-ADTActiveSetup -Key 'ProgramUserConfig' -PurgeActiveSetupKey
+
+ Delete "ProgramUserConfig" active setup entry from all registry hives.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Original code borrowed from: Denis St-Pierre (Ottawa, Canada), Todd MacNaught (Ottawa, Canada)
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTActiveSetup
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'Create')]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Create')]
+ [ValidateScript({
+ if (('.exe', '.vbs', '.cmd', '.bat', '.ps1', '.js') -notcontains ($StubExeExt = [System.IO.Path]::GetExtension($_)))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName StubExePath -ProvidedValue $_ -ExceptionMessage "Unsupported Active Setup StubPath file extension [$StubExeExt]."))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$StubExePath,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Arguments,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = '(Get-ExecutionPolicy)')]
+ [Microsoft.PowerShell.ExecutionPolicy]$ExecutionPolicy,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Version = [System.DateTime]::Now.ToString('yyMM,ddHH,mmss'), # Ex: 1405,1515,0522
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Locale,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [System.Management.Automation.SwitchParameter]$DisableActiveSetup,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Purge')]
+ [System.Management.Automation.SwitchParameter]$PurgeActiveSetupKey,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Create')]
+ [System.Management.Automation.SwitchParameter]$NoExecuteForCurrentUser
+ )
+
+ dynamicparam
+ {
+ # Attempt to get the most recent ADTSession object.
+ $adtSession = if (& $Script:CommandTable.'Test-ADTSessionActive')
+ {
+ & $Script:CommandTable.'Get-ADTSession'
+ }
+
+ # Define parameter dictionary for returning at the end.
+ $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
+
+ # Add in parameters we need as mandatory when there's no active ADTSession.
+ $paramDictionary.Add('Key', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Key', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Name of the registry key for the Active Setup entry. Defaults to active session InstallName.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('Description', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Description', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Description for the Active Setup. Users will see "Setting up personalized settings for: $Description" at logon. Defaults to active session InstallName.'; ParameterSetName = 'Create' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+
+ # Return the populated dictionary.
+ return $paramDictionary
+ }
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Set defaults for when there's an active ADTSession and overriding values haven't been specified.
+ $Description = if ($PSCmdlet.ParameterSetName.Equals('Create'))
+ {
+ if (!$PSBoundParameters.ContainsKey('Description'))
+ {
+ $adtSession.InstallName
+ }
+ else
+ {
+ $PSBoundParameters.Description
+ }
+ }
+ $Key = if (!$PSBoundParameters.ContainsKey('Key'))
+ {
+ $adtSession.InstallName
+ }
+ else
+ {
+ $PSBoundParameters.Key
+ }
+
+ # Define initial variables.
+ $ActiveSetupFileName = [System.IO.Path]::GetFileName($StubExePath)
+ $runAsActiveUser = & $Script:CommandTable.'Get-ADTRunAsActiveUser'
+ $CUStubExePath = $null
+ $CUArguments = $null
+ $StubExeExt = [System.IO.Path]::GetExtension($StubExePath)
+ $StubPath = $null
+
+ # Define internal function to test current ActiveSetup stuff.
+ function Test-ADTActiveSetup
+ {
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$HKLMKey,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$HKCUKey,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID
+ )
+
+ # Set up initial variables.
+ $HKCUProps = if ($SID)
+ {
+ & $Script:CommandTable.'Get-ADTRegistryKey' -Key $HKCUKey -SID $SID
+ }
+ else
+ {
+ & $Script:CommandTable.'Get-ADTRegistryKey' -Key $HKCUKey
+ }
+ $HKLMProps = & $Script:CommandTable.'Get-ADTRegistryKey' -Key $HKLMKey
+ $HKCUVer = $HKCUProps | & $Script:CommandTable.'Select-Object' -ExpandProperty Version -ErrorAction Ignore
+ $HKLMVer = $HKLMProps | & $Script:CommandTable.'Select-Object' -ExpandProperty Version -ErrorAction Ignore
+ $HKLMInst = $HKLMProps | & $Script:CommandTable.'Select-Object' -ExpandProperty IsInstalled -ErrorAction Ignore
+
+ # HKLM entry not present. Nothing to run.
+ if (!$HKLMProps)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM active setup entry is not present.'
+ return $false
+ }
+
+ # HKLM entry present, but disabled. Nothing to run.
+ if ($HKLMInst -eq 0)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM active setup entry is present, but it is disabled (IsInstalled set to 0).'
+ return $false
+ }
+
+ # HKLM entry present and HKCU entry is not. Run the StubPath.
+ if (!$HKCUProps)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM active setup entry is present. HKCU active setup entry is not present.'
+ return $true
+ }
+
+ # Both entries present. HKLM entry does not have Version property. Nothing to run.
+ if (!$HKLMVer)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM and HKCU active setup entries are present. HKLM Version property is missing.'
+ return $false
+ }
+
+ # Both entries present. HKLM entry has Version property, but HKCU entry does not. Run the StubPath.
+ if (!$HKCUVer)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM and HKCU active setup entries are present. HKCU Version property is missing.'
+ return $true
+ }
+
+ # After cleanup, the HKLM Version property is empty. Considering it missing. HKCU is present so nothing to run.
+ if (!([System.Object]$HKLMValidVer = [System.String]::Join($null, ($HKLMVer.GetEnumerator() | & { process { if ([System.Char]::IsDigit($_)) { return $_ } elseif ($_ -eq ',') { return '.' } } }))) -or ![System.Version]::TryParse($HKLMValidVer, [ref]$HKLMValidVer))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM and HKCU active setup entries are present. HKLM Version property is invalid.'
+ return $false
+ }
+
+ # After cleanup, the HKCU Version property is empty while HKLM Version property is not. Run the StubPath.
+ if (!([System.Object]$HKCUValidVer = [System.String]::Join($null, ($HKCUVer.GetEnumerator() | & { process { if ([System.Char]::IsDigit($_)) { return $_ } elseif ($_ -eq ',') { return '.' } } }))) -or ![System.Version]::TryParse($HKCUValidVer, [ref]$HKCUValidVer))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM and HKCU active setup entries are present. HKCU Version property is invalid.'
+ return $true
+ }
+
+ # Both entries present, with a Version property. Compare the Versions.
+ if ($HKLMValidVer -gt $HKCUValidVer)
+ {
+ # HKLM is greater, run the StubPath.
+ & $Script:CommandTable.'Write-ADTLogEntry' "HKLM and HKCU active setup entries are present. Both contain Version properties, and the HKLM Version is greater."
+ return $true
+ }
+ else
+ {
+ # The HKCU version is equal or higher than HKLM version, Nothing to run.
+ & $Script:CommandTable.'Write-ADTLogEntry' 'HKLM and HKCU active setup entries are present. Both contain Version properties. However, they are either the same or the HKCU Version property is higher.'
+ return $false
+ }
+ }
+
+ # Define internal function to the required ActiveSetup registry keys.
+ function Set-ADTActiveSetupRegistryEntry
+ {
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'This is an internal worker function that requires no end user confirmation.')]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$RegPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Version,
+
+ [Parameter(Mandatory = $false)]
+ [AllowEmptyString()]
+ [System.String]$Locale,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DisableActiveSetup
+ )
+
+ $srkParams = if ($SID) { @{ SID = $SID } } else { @{} }
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key $RegPath -Name '(Default)' -Value $Description @srkParams
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key $RegPath -Name 'Version' -Value $Version @srkParams
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key $RegPath -Name 'StubPath' -Value $StubPath -Type 'String' @srkParams
+ if (![System.String]::IsNullOrWhiteSpace($Locale))
+ {
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key $RegPath -Name 'Locale' -Value $Locale @srkParams
+ }
+
+ # Only Add IsInstalled to HKLM.
+ if ($RegPath.Contains('HKEY_LOCAL_MACHINE'))
+ {
+ & $Script:CommandTable.'Set-ADTRegistryKey' -Key $RegPath -Name 'IsInstalled' -Value ([System.UInt32]!$DisableActiveSetup) -Type 'DWord' @srkParams
+ }
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Set up the relevant keys, factoring in bitness and architecture.
+ if ($Wow6432Node -and [System.Environment]::Is64BitOperatingSystem)
+ {
+ $HKLMRegKey = "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Active Setup\Installed Components\$Key"
+ $HKCURegKey = "Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Wow6432Node\Microsoft\Active Setup\Installed Components\$Key"
+ }
+ else
+ {
+ $HKLMRegKey = "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Active Setup\Installed Components\$Key"
+ $HKCURegKey = "Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Active Setup\Installed Components\$Key"
+ }
+
+ # Delete Active Setup registry entry from the HKLM hive and for all logon user registry hives on the system.
+ if ($PurgeActiveSetupKey)
+ {
+ # HLKM first.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing Active Setup entry [$HKLMRegKey]."
+ & $Script:CommandTable.'Remove-ADTRegistryKey' -Key $HKLMRegKey -Recurse
+
+ # All remaining users thereafter.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing Active Setup entry [$HKCURegKey] for all logged on user registry hives on the system."
+ & $Script:CommandTable.'Invoke-ADTAllUsersRegistryAction' -UserProfiles (& $Script:CommandTable.'Get-ADTUserProfiles' -ExcludeDefaultUser | & { process { if ($_.SID -eq $runAsActiveUser.SID) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1) -ScriptBlock {
+ if (& $Script:CommandTable.'Get-ADTRegistryKey' -Key $HKCURegKey -SID $_.SID)
+ {
+ & $Script:CommandTable.'Remove-ADTRegistryKey' -Key $HKCURegKey -SID $_.SID -Recurse
+ }
+ }
+ return
+ }
+
+ # Copy file to $StubExePath from the 'Files' subdirectory of the script directory (if it exists there).
+ $StubExePath = [System.Environment]::ExpandEnvironmentVariables($StubExePath)
+ if ($adtSession -and $adtSession.DirFiles)
+ {
+ $StubExeFile = & $Script:CommandTable.'Join-Path' -Path $adtSession.DirFiles -ChildPath $ActiveSetupFileName
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath $StubExeFile -PathType Leaf)
+ {
+ # This will overwrite the StubPath file if $StubExePath already exists on target.
+ & $Script:CommandTable.'Copy-ADTFile' -Path $StubExeFile -Destination $StubExePath -ErrorAction Stop
+ }
+ }
+
+ # Check if the $StubExePath file exists.
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $StubExePath -PathType Leaf))
+ {
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("Active Setup StubPath file [$ActiveSetupFileName] is missing.")
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'ActiveSetupFileNotFound'
+ TargetObject = $ActiveSetupFileName
+ RecommendedAction = "Please confirm the provided value and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Define Active Setup StubPath according to file extension of $StubExePath.
+ switch ($StubExeExt)
+ {
+ '.exe'
+ {
+ $CUStubExePath = $StubExePath
+ $CUArguments = $Arguments
+ $StubPath = if ([System.String]::IsNullOrWhiteSpace($Arguments))
+ {
+ "`"$CUStubExePath`""
+ }
+ else
+ {
+ "`"$CUStubExePath`" $CUArguments"
+ }
+ break
+ }
+ { $_ -in '.js', '.vbs' }
+ {
+ $CUStubExePath = "$([System.Environment]::SystemDirectory)\wscript.exe"
+ $CUArguments = if ([System.String]::IsNullOrWhiteSpace($Arguments))
+ {
+ "//nologo `"$StubExePath`""
+ }
+ else
+ {
+ "//nologo `"$StubExePath`" $Arguments"
+ }
+ $StubPath = "`"$CUStubExePath`" $CUArguments"
+ break
+ }
+ { $_ -in '.cmd', '.bat' }
+ {
+ $CUStubExePath = "$([System.Environment]::SystemDirectory)\cmd.exe"
+ # Prefix any CMD.exe metacharacters ^ or & with ^ to escape them - parentheses only require escaping when there's no space in the path!
+ $StubExePath = if ($StubExePath.Trim() -match '\s')
+ {
+ $StubExePath -replace '([&^])', '^$1'
+ }
+ else
+ {
+ $StubExePath -replace '([()&^])', '^$1'
+ }
+ $CUArguments = if ([System.String]::IsNullOrWhiteSpace($Arguments))
+ {
+ "/C `"$StubExePath`""
+ }
+ else
+ {
+ "/C `"`"$StubExePath`" $Arguments`""
+ }
+ $StubPath = "`"$CUStubExePath`" $CUArguments"
+ break
+ }
+ '.ps1'
+ {
+ $CUStubExePath = & $Script:CommandTable.'Get-ADTPowerShellProcessPath'
+ $CUArguments = if ([System.String]::IsNullOrWhiteSpace($Arguments))
+ {
+ "$(if ($PSBoundParameters.ContainsKey('ExecutionPolicy')) { "-ExecutionPolicy $ExecutionPolicy " })-NoProfile -NoLogo -WindowStyle Hidden -File `"$StubExePath`""
+ }
+ else
+ {
+ "$(if ($PSBoundParameters.ContainsKey('ExecutionPolicy')) { "-ExecutionPolicy $ExecutionPolicy " })-NoProfile -NoLogo -WindowStyle Hidden -File `"$StubExePath`" $Arguments"
+ }
+ $StubPath = "`"$CUStubExePath`" $CUArguments"
+ break
+ }
+ }
+
+ # Define common parameters split for Set-ADTActiveSetupRegistryEntry.
+ $sasreParams = @{
+ Version = $Version
+ Locale = $Locale
+ DisableActiveSetup = $DisableActiveSetup
+ }
+
+ # Create the Active Setup entry in the registry.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Adding Active Setup Key for local machine: [$HKLMRegKey]."
+ Set-ADTActiveSetupRegistryEntry @sasreParams -RegPath $HKLMRegKey
+
+ # Execute the StubPath file for the current user as long as not in Session 0.
+ if ($NoExecuteForCurrentUser)
+ {
+ return
+ }
+
+ if ([System.Security.Principal.WindowsIdentity]::GetCurrent().User.IsWellKnown([System.Security.Principal.WellKnownSidType]::LocalSystemSid))
+ {
+ if (!$runAsActiveUser)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Session 0 detected: No logged in users detected. Active Setup StubPath file will execute when users first log into their account.'
+ return
+ }
+
+ # Skip if Active Setup reg key is present and Version is equal or higher
+ if (!(Test-ADTActiveSetup -HKLMKey $HKLMRegKey -HKCUKey $HKCURegKey -SID $runAsActiveUser.SID))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Session 0 detected: Skipping executing Active Setup StubPath file for currently logged in user [$($runAsActiveUser.NTAccount)]." -Severity 2
+ return
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Session 0 detected: Executing Active Setup StubPath file for currently logged in user [$($runAsActiveUser.NTAccount)]."
+ if ($CUArguments)
+ {
+ & $Script:CommandTable.'Start-ADTProcessAsUser' -FilePath $CUStubExePath -ArgumentList $CUArguments -Wait -HideWindow
+ }
+ else
+ {
+ & $Script:CommandTable.'Start-ADTProcessAsUser' -FilePath $CUStubExePath -Wait -HideWindow
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Adding Active Setup Key for the current user: [$HKCURegKey]."
+ Set-ADTActiveSetupRegistryEntry @sasreParams -RegPath $HKCURegKey -SID $runAsActiveUser.SID
+ }
+ else
+ {
+ # Skip if Active Setup reg key is present and Version is equal or higher
+ if (!(Test-ADTActiveSetup -HKLMKey $HKLMRegKey -HKCUKey $HKCURegKey))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Skipping executing Active Setup StubPath file for current user.' -Severity 2
+ return
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Executing Active Setup StubPath file for the current user.'
+ if ($CUArguments)
+ {
+ if ($StubExeExt -eq '.ps1')
+ {
+ $CUArguments = $CUArguments.Replace("-WindowStyle Hidden ", $null)
+ }
+ & $Script:CommandTable.'Start-ADTProcess' -FilePath $CUStubExePath -ArgumentList $CUArguments
+ }
+ else
+ {
+ & $Script:CommandTable.'Start-ADTProcess' -FilePath $CUStubExePath
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Adding Active Setup Key for the current user: [$HKCURegKey]."
+ Set-ADTActiveSetupRegistryEntry @sasreParams -RegPath $HKCURegKey
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to set Active Setup registry entry."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTDeferHistory
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTDeferHistory
+{
+ <#
+ .SYNOPSIS
+ Set the history of deferrals in the registry for the current application.
+
+ .DESCRIPTION
+ Set the history of deferrals in the registry for the current application.
+
+ .PARAMETER DeferTimesRemaining
+ Specify the number of deferrals remaining.
+
+ .PARAMETER DeferDeadline
+ Specify the deadline for the deferral.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Set-DeferHistory
+
+ .NOTES
+ An active ADT session is required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTDeferHistory
+
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32]$DeferTimesRemaining,
+
+ [Parameter(Mandatory = $false)]
+ [AllowEmptyString()]
+ [System.String]$DeferDeadline
+ )
+
+ try
+ {
+ (& $Script:CommandTable.'Get-ADTSession').SetDeferHistory($(if ($PSBoundParameters.ContainsKey('DeferTimesRemaining')) { $DeferTimesRemaining }), $DeferDeadline)
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTIniValue
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTIniValue
+{
+ <#
+ .SYNOPSIS
+ Opens an INI file and sets the value of the specified section and key.
+
+ .DESCRIPTION
+ Opens an INI file and sets the value of the specified section and key. If the value is set to $null, the key will be removed from the section.
+
+ .PARAMETER FilePath
+ Path to the INI file.
+
+ .PARAMETER Section
+ Section within the INI file.
+
+ .PARAMETER Key
+ Key within the section of the INI file.
+
+ .PARAMETER Value
+ Value for the key within the section of the INI file. To remove a value, set this variable to $null.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Set-ADTIniValue -FilePath "$env:ProgramFilesX86\IBM\Notes\notes.ini" -Section 'Notes' -Key 'KeyFileName' -Value 'MyFile.ID'
+
+ Sets the 'KeyFileName' key in the 'Notes' section of the 'notes.ini' file to 'MyFile.ID'.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTIniValue
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Section,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $true)]
+ [AllowNull()]
+ [System.Object]$Value
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Writing INI Key Value: [Section = $Section] [Key = $Key] [Value = $Value]."
+ try
+ {
+ try
+ {
+ [PSADT.Configuration.IniFile]::WriteSectionKeyValue($Section, $Key, $Value, $FilePath)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to write INI file key value."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTItemPermission
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTItemPermission
+{
+ <#
+ .SYNOPSIS
+ Allows you to easily change permissions on files or folders.
+
+ .DESCRIPTION
+ Allows you to easily change permissions on files or folders for a given user or group. You can add, remove or replace permissions, set inheritance and propagation.
+
+ .PARAMETER Path
+ Path to the folder or file you want to modify (ex: C:\Temp)
+
+ .PARAMETER User
+ One or more user names (ex: BUILTIN\Users, DOMAIN\Admin) to give the permissions to. If you want to use SID, prefix it with an asterisk * (ex: *S-1-5-18)
+
+ .PARAMETER Permission
+ Permission or list of permissions to be set/added/removed/replaced. Permission DeleteSubdirectoriesAndFiles does not apply to files.
+
+ .PARAMETER PermissionType
+ Sets Access Control Type of the permissions.
+
+ .PARAMETER Inheritance
+ Sets permission inheritance. Does not apply to files. Multiple options can be specified.
+
+ * None - The permission entry is not inherited by child objects.
+ * ObjectInherit - The permission entry is inherited by child leaf objects.
+ * ContainerInherit - The permission entry is inherited by child container objects.
+
+ .PARAMETER Propagation
+ Sets how to propagate inheritance. Does not apply to files.
+
+ * None - Specifies that no inheritance flags are set.
+ * NoPropagateInherit - Specifies that the permission entry is not propagated to child objects.
+ * InheritOnly - Specifies that the permission entry is propagated only to child objects. This includes both container and leaf child objects.
+
+ .PARAMETER Method
+ Specifies which method will be used to apply the permissions.
+
+ * AddAccessRule - Adds permissions rules but it does not remove previous permissions.
+ * SetAccessRule - Overwrites matching permission rules with new ones.
+ * ResetAccessRule - Removes matching permissions rules and then adds permission rules.
+ * RemoveAccessRule - Removes matching permission rules.
+ * RemoveAccessRuleAll - Removes all permission rules for specified user/s.
+ * RemoveAccessRuleSpecific - Removes specific permissions.
+
+ .PARAMETER EnableInheritance
+ Enables inheritance on the files/folders.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Set-ADTItemPermission -Path 'C:\Temp' -User 'DOMAIN\John', 'BUILTIN\Users' -Permission FullControl -Inheritance ObjectInherit,ContainerInherit
+
+ Will grant FullControl permissions to 'John' and 'Users' on 'C:\Temp' and its files and folders children.
+
+ .EXAMPLE
+ Set-ADTItemPermission -Path 'C:\Temp\pic.png' -User 'DOMAIN\John' -Permission 'Read'
+
+ Will grant Read permissions to 'John' on 'C:\Temp\pic.png'.
+
+ .EXAMPLE
+ Set-ADTItemPermission -Path 'C:\Temp\Private' -User 'DOMAIN\John' -Permission 'None' -Method 'RemoveAll'
+
+ Will remove all permissions to 'John' on 'C:\Temp\Private'.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Original Author: Julian DA CUNHA - dacunha.julian@gmail.com, used with permission.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTItemPermission
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Path to the folder or file you want to modify (ex: C:\Temp)', ParameterSetName = 'DisableInheritance')]
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Path to the folder or file you want to modify (ex: C:\Temp)', ParameterSetName = 'EnableInheritance')]
+ [ValidateScript({
+ if (!(& $Script:CommandTable.'Test-Path' -Path $_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified path does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [Alias('File', 'Folder')]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'One or more user names (ex: BUILTIN\Users, DOMAIN\Admin). If you want to use SID, prefix it with an asterisk * (ex: *S-1-5-18)', ParameterSetName = 'DisableInheritance')]
+ [Alias('Username', 'Users', 'SID', 'Usernames')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$User,
+
+ [Parameter(Mandatory = $true, Position = 2, HelpMessage = "Permission or list of permissions to be set/added/removed/replaced. To see all the possible permissions go to 'http://technet.microsoft.com/fr-fr/library/ff730951.aspx'", ParameterSetName = 'DisableInheritance')]
+ [Alias('Acl', 'Grant', 'Permissions', 'Deny')]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.AccessControl.FileSystemRights]$Permission,
+
+ [Parameter(Mandatory = $false, Position = 3, HelpMessage = 'Whether you want to set Allow or Deny permissions', ParameterSetName = 'DisableInheritance')]
+ [Alias('AccessControlType')]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.AccessControl.AccessControlType]$PermissionType = [System.Security.AccessControl.AccessControlType]::Allow,
+
+ [Parameter(Mandatory = $false, Position = 4, HelpMessage = 'Sets how permissions are inherited', ParameterSetName = 'DisableInheritance')]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.AccessControl.InheritanceFlags]$Inheritance = [System.Security.AccessControl.InheritanceFlags]::None,
+
+ [Parameter(Mandatory = $false, Position = 5, HelpMessage = 'Sets how to propage inheritance flags', ParameterSetName = 'DisableInheritance')]
+ [ValidateNotNullOrEmpty()]
+ [System.Security.AccessControl.PropagationFlags]$Propagation = [System.Security.AccessControl.PropagationFlags]::None,
+
+ [Parameter(Mandatory = $false, Position = 6, HelpMessage = 'Specifies which method will be used to add/remove/replace permissions.', ParameterSetName = 'DisableInheritance')]
+ [ValidateSet('AddAccessRule', 'SetAccessRule', 'ResetAccessRule', 'RemoveAccessRule', 'RemoveAccessRuleAll', 'RemoveAccessRuleSpecific')]
+ [Alias('ApplyMethod', 'ApplicationMethod')]
+ [System.String]$Method = 'AddAccessRule',
+
+ [Parameter(Mandatory = $true, Position = 1, HelpMessage = 'Enables inheritance, which removes explicit permissions.', ParameterSetName = 'EnableInheritance')]
+ [System.Management.Automation.SwitchParameter]$EnableInheritance
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Get object ACLs and enable inheritance.
+ if ($EnableInheritance)
+ {
+ ($Acl = & $Script:CommandTable.'Get-Acl' -Path $Path).SetAccessRuleProtection($false, $true)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Enabling Inheritance on path [$Path]."
+ $null = & $Script:CommandTable.'Set-Acl' -Path $Path -AclObject $Acl
+ return
+ }
+
+ # Modify variables to remove file incompatible flags if this is a file.
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath $Path -PathType Leaf)
+ {
+ $Permission = $Permission -band (-bnot [System.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles)
+ $Inheritance = [System.Security.AccessControl.InheritanceFlags]::None
+ $Propagation = [System.Security.AccessControl.PropagationFlags]::None
+ }
+
+ # Get object ACLs, disable inheritance but preserve inherited permissions.
+ ($Acl = & $Script:CommandTable.'Get-Acl' -Path $Path).SetAccessRuleProtection($true, $true)
+ $null = & $Script:CommandTable.'Set-Acl' -Path $Path -AclObject $Acl
+
+ # Get updated ACLs - without inheritance.
+ $Acl = & $Script:CommandTable.'Get-Acl' -Path $Path
+
+ # Apply permissions on each user.
+ foreach ($Username in $User.Trim())
+ {
+ # Return early if the string is empty.
+ if ([System.String]::IsNullOrWhiteSpace($Username))
+ {
+ continue
+ }
+
+ # Translate a SID to NTAccount.
+ if ($Username.StartsWith('*') -and !($Username = & $Script:CommandTable.'ConvertTo-ADTNTAccountOrSID' -SID $Username.Remove(0, 1)))
+ {
+ continue
+ }
+
+ # Set/Add/Remove/Replace permissions and log the changes.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Changing permissions [Permissions:$Permission, InheritanceFlags:$Inheritance, PropagationFlags:$Propagation, AccessControlType:$PermissionType, Method:$Method] on path [$Path] for user [$Username]."
+ $Acl.$Method([System.Security.AccessControl.FileSystemAccessRule]::new($Username, $Permission, $Inheritance, $Propagation, $PermissionType))
+ }
+
+ # Use the prepared ACL.
+ $null = & $Script:CommandTable.'Set-Acl' -Path $Path -AclObject $Acl
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTMsiProperty
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTMsiProperty
+{
+ <#
+ .SYNOPSIS
+ Set a property in the MSI property table.
+
+ .DESCRIPTION
+ Set a property in the MSI property table.
+
+ .PARAMETER Database
+ Specify a ComObject representing an MSI database opened in view/modify/update mode.
+
+ .PARAMETER PropertyName
+ The name of the property to be set/modified.
+
+ .PARAMETER PropertyValue
+ The value of the property to be set/modified.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Set-ADTMsiProperty -Database $TempMsiPathDatabase -PropertyName 'ALLUSERS' -PropertyValue '1'
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Original Author: Julian DA CUNHA - dacunha.julian@gmail.com, used with permission.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTMsiProperty
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.__ComObject]$Database,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$PropertyName,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$PropertyValue
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Setting the MSI Property Name [$PropertyName] with Property Value [$PropertyValue]."
+ try
+ {
+ try
+ {
+ # Open the requested table view from the database.
+ $View = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Database -MethodName OpenView -ArgumentList @("SELECT * FROM Property WHERE Property='$PropertyName'")
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $View -MethodName Execute
+
+ # Retrieve the requested property from the requested table and close off the view.
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa371136(v=vs.85).aspx
+ $Record = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $View -MethodName Fetch
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $View -MethodName Close -ArgumentList @()
+ $null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($View)
+
+ # Set the MSI property.
+ $View = if ($Record)
+ {
+ # If the property already exists, then create the view for updating the property.
+ & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Database -MethodName OpenView -ArgumentList @("UPDATE Property SET Value='$PropertyValue' WHERE Property='$PropertyName'")
+ }
+ else
+ {
+ # If property does not exist, then create view for inserting the property.
+ & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Database -MethodName OpenView -ArgumentList @("INSERT INTO Property (Property, Value) VALUES ('$PropertyName','$PropertyValue')")
+ }
+ $null = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $View -MethodName Execute
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to set the MSI Property Name [$PropertyName] with Property Value [$PropertyValue]."
+ }
+ finally
+ {
+ $null = try
+ {
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath Microsoft.PowerShell.Core\Variable::View)
+ {
+ & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $View -MethodName Close -ArgumentList @()
+ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($View)
+ }
+ }
+ catch
+ {
+ $null
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTPowerShellCulture
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTPowerShellCulture
+{
+ <#
+ .SYNOPSIS
+ Changes the current thread's Culture and UICulture to the specified culture.
+
+ .DESCRIPTION
+ This function changes the current thread's Culture and UICulture to the specified culture.
+
+ .PARAMETER CultureInfo
+ The culture to set the current thread's Culture and UICulture to. Can be a CultureInfo object, or any valid IETF BCP 47 language tag.
+
+ .EXAMPLE
+ Set-ADTPowerShellCulture -Culture en-US
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTPowerShellCulture
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.Globalization.CultureInfo]$CultureInfo
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $smaCultureResolver = [System.Reflection.Assembly]::Load('System.Management.Automation').GetType('Microsoft.PowerShell.NativeCultureResolver')
+ $smaResolverFlags = [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static
+ [System.Globalization.CultureInfo[]]$validCultures = (& $Script:CommandTable.'Get-WinUserLanguageList').LanguageTag
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Test that the specified culture is installed or not.
+ if (!$validCultures.Contains($CultureInfo))
+ {
+ $naerParams = @{
+ Exception = [System.ArgumentException]::new("The language pack for [$CultureInfo] is not installed on this system.", $CultureInfo)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidArgument
+ ErrorId = 'CultureNotInstalled'
+ TargetObject = $validCultures
+ RecommendedAction = "Please review the installed cultures within this error's TargetObject and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Reflectively update the culture to the specified value.
+ # This will change PowerShell, but not its default variables like $PSCulture and $PSUICulture.
+ $smaCultureResolver.GetField('m_Culture', $smaResolverFlags).SetValue($null, $CultureInfo)
+ $smaCultureResolver.GetField('m_uiCulture', $smaResolverFlags).SetValue($null, $CultureInfo)
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTRegistryKey
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTRegistryKey
+{
+ <#
+ .SYNOPSIS
+ Creates or sets a registry key name, value, and value data.
+
+ .DESCRIPTION
+ Creates a registry key name, value, and value data; it sets the same if it already exists. This function can also handle registry keys for specific user SIDs and 32-bit registry on 64-bit systems.
+
+ .PARAMETER Key
+ The registry key path.
+
+ .PARAMETER Name
+ The value name.
+
+ .PARAMETER Value
+ The value data.
+
+ .PARAMETER Type
+ The type of registry value to create or set. Options: 'Binary','DWord','ExpandString','MultiString','None','QWord','String','Unknown'. Default: String.
+
+ DWord should be specified as a decimal.
+
+ .PARAMETER Wow6432Node
+ Specify this switch to write to the 32-bit registry (Wow6432Node) on 64-bit systems.
+
+ .PARAMETER SID
+ The security identifier (SID) for a user. Specifying this parameter will convert a HKEY_CURRENT_USER registry key to the HKEY_USERS\$SID format.
+
+ Specify this parameter from the Invoke-ADTAllUsersRegistryAction function to read/edit HKCU registry settings for all users on the system.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Set-ADTRegistryKey -Key $blockedAppPath -Name 'Debugger' -Value $blockedAppDebuggerValue
+
+ Creates or sets the 'Debugger' value in the specified registry key.
+
+ .EXAMPLE
+ Set-ADTRegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE' -Name 'Application' -Type 'DWord' -Value '1'
+
+ Creates or sets a DWord value in the specified registry key.
+
+ .EXAMPLE
+ Set-ADTRegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce' -Name 'Debugger' -Value $blockedAppDebuggerValue -Type String
+
+ Creates or sets a String value in the specified registry key.
+
+ .EXAMPLE
+ Set-ADTRegistryKey -Key 'HKCU\Software\Microsoft\Example' -Name 'Data' -Value (0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,0x02,0x02,0x02) -Type 'Binary'
+
+ Creates or sets a Binary value in the specified registry key.
+
+ .EXAMPLE
+ Set-ADTRegistryKey -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Example' -Name '(Default)' -Value "Text"
+
+ Creates or sets the default value in the specified registry key.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTRegistryKey
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Object]$Value,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Binary', 'DWord', 'ExpandString', 'MultiString', 'None', 'QWord', 'String', 'Unknown')]
+ [Microsoft.Win32.RegistryValueKind]$Type = 'String',
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If the SID variable is specified, then convert all HKEY_CURRENT_USER key's to HKEY_USERS\$SID.
+ $Key = if ($PSBoundParameters.ContainsKey('SID'))
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key -Wow6432Node:$Wow6432Node -SID $SID
+ }
+ else
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key -Wow6432Node:$Wow6432Node
+ }
+
+ # Create registry key if it doesn't exist.
+ if (!(& $Script:CommandTable.'Test-Path' -LiteralPath $Key))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating registry key [$Key]."
+ if (($Key.Split('/').Count - 1) -eq 0)
+ {
+ # No forward slash found in Key. Use New-Item cmdlet to create registry key.
+ $null = & $Script:CommandTable.'New-Item' -Path $Key -ItemType Registry -Force
+ }
+ else
+ {
+ # Forward slash was found in Key. Use REG.exe ADD to create registry key
+ $RegMode = if ([System.Environment]::Is64BitProcess -and !$Wow6432Node)
+ {
+ '/reg:64'
+ }
+ else
+ {
+ '/reg:32'
+ }
+ $null = & "$([System.Environment]::SystemDirectory)\reg.exe" ADD "$($Key.Substring($Key.IndexOf('::') + 2))" /f $RegMode 2>&1
+ }
+ }
+
+ if ($Name)
+ {
+ if (!(& $Script:CommandTable.'Get-ItemProperty' -LiteralPath $Key -Name $Name -ErrorAction Ignore))
+ {
+ # Set registry value if it doesn't exist.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Setting registry key value: [$Key] [$Name = $Value]."
+ $null = & $Script:CommandTable.'New-ItemProperty' -LiteralPath $Key -Name $Name -Value $Value -PropertyType $Type
+ }
+ else
+ {
+ # Update registry value if it does exist.
+ if ($Name -eq '(Default)')
+ {
+ # Set Default registry key value with the following workaround, because Set-ItemProperty contains a bug and cannot set Default registry key value.
+ $null = (& $Script:CommandTable.'Get-Item' -LiteralPath $Key).OpenSubKey('', 'ReadWriteSubTree').SetValue($null, $Value)
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Updating registry key value: [$Key] [$Name = $Value]."
+ $null = & $Script:CommandTable.'Set-ItemProperty' -LiteralPath $Key -Name $Name -Value $Value
+ }
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to $(("set registry key [$Key]", "update value [$Value] for registry key [$Key] [$Name]")[!!$Name])."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTServiceStartMode
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTServiceStartMode
+{
+ <#
+ .SYNOPSIS
+ Set the service startup mode.
+
+ .DESCRIPTION
+ Set the service startup mode. This function allows you to configure the startup mode of a specified service. The startup modes available are: Automatic, Automatic (Delayed Start), Manual, Disabled, Boot, and System.
+
+ .PARAMETER Service
+ Specify the name of the service.
+
+ .PARAMETER StartMode
+ Specify startup mode for the service. Options: Automatic, Automatic (Delayed Start), Manual, Disabled, Boot, System.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Set-ADTServiceStartMode -Service 'wuauserv' -StartMode 'Automatic (Delayed Start)'
+
+ Sets the 'wuauserv' service to start automatically with a delayed start.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTServiceStartMode
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (!$_.Name)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Service -ProvidedValue $_ -ExceptionMessage 'The specified service does not exist.'))
+ }
+ return !!$_
+ })]
+ [System.ServiceProcess.ServiceController]$Service,
+
+ [Parameter(Mandatory = $true)]
+ [ValidateSet('Automatic', 'Automatic (Delayed Start)', 'Manual', 'Disabled', 'Boot', 'System')]
+ [System.String]$StartMode
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ # Re-write StartMode to suit sc.exe.
+ & $Script:CommandTable.'New-Variable' -Name StartMode -Force -Confirm:$false -Value $(switch ($StartMode)
+ {
+ 'Automatic' { 'Auto'; break }
+ 'Automatic (Delayed Start)' { 'Delayed-Auto'; break }
+ 'Manual' { 'Demand'; break }
+ default { $_; break }
+ })
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$(($msg = "Setting service [$($Service.Name)] startup mode to [$StartMode]"))."
+ try
+ {
+ try
+ {
+ # Set the start up mode using sc.exe. Note: we found that the ChangeStartMode method in the Win32_Service WMI class set services to 'Automatic (Delayed Start)' even when you specified 'Automatic' on Win7, Win8, and Win10.
+ $scResult = & "$([System.Environment]::SystemDirectory)\sc.exe" config $Service.Name start= $StartMode 2>&1
+ if (!$Global:LASTEXITCODE)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Successfully set service [($Service.Name)] startup mode to [$StartMode]."
+ return
+ }
+
+ # If we're here, we had a bad exit code.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message ($msg = "$msg failed with exit code [$Global:LASTEXITCODE]: $scResult") -Severity 3
+ $naerParams = @{
+ Exception = [System.Runtime.InteropServices.ExternalException]::new($msg, $Global:LASTEXITCODE)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'ScConfigFailure'
+ TargetObject = $scResult
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Set-ADTShortcut
+#
+#-----------------------------------------------------------------------------
+
+function Set-ADTShortcut
+{
+ <#
+ .SYNOPSIS
+ Modifies a .lnk or .url type shortcut.
+
+ .DESCRIPTION
+ Modifies a shortcut - .lnk or .url file, with configurable options. Only specify the parameters that you want to change.
+
+ .PARAMETER Path
+ Path to the shortcut to be changed.
+
+ .PARAMETER TargetPath
+ Sets target path or URL that the shortcut launches.
+
+ .PARAMETER Arguments
+ Sets the arguments used against the target path.
+
+ .PARAMETER IconLocation
+ Sets location of the icon used for the shortcut.
+
+ .PARAMETER IconIndex
+ Sets the index of the icon. Executables, DLLs, ICO files with multiple icons need the icon index to be specified. This parameter is an Integer. The first index is 0.
+
+ .PARAMETER Description
+ Sets the description of the shortcut as can be seen in the shortcut's properties.
+
+ .PARAMETER WorkingDirectory
+ Sets working directory to be used for the target path.
+
+ .PARAMETER WindowStyle
+ Sets the shortcut's window style to be minimised, maximised, etc.
+
+ .PARAMETER RunAsAdmin
+ Sets the shortcut to require elevated permissions to run.
+
+ .PARAMETER HotKey
+ Sets the hotkey to launch the shortcut, e.g. "CTRL+SHIFT+F".
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Set-ADTShortcut -Path "$envCommonDesktop\Application.lnk" -TargetPath "$envProgramFiles\Application\application.exe"
+
+ Creates a shortcut on the All Users desktop named 'Application', targeted to '$envProgramFiles\Application\application.exe'.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Set-ADTShortcut
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_) -or (![System.IO.Path]::GetExtension($_).ToLower().Equals('.lnk') -and ![System.IO.Path]::GetExtension($_).ToLower().Equals('.url')))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Path -ProvidedValue $_ -ExceptionMessage 'The specified path does not exist or does not have the correct extension.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$Path,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$TargetPath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Arguments,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$IconLocation,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$IconIndex,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Description,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Normal', 'Maximized', 'Minimized', 'DontChange')]
+ [System.String]$WindowStyle = 'DontChange',
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$RunAsAdmin,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Hotkey
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Changing shortcut [$Path]."
+ try
+ {
+ try
+ {
+ # Make sure .NET's current directory is synced with PowerShell's.
+ [System.IO.Directory]::SetCurrentDirectory((& $Script:CommandTable.'Get-Location' -PSProvider FileSystem).ProviderPath)
+ if ([System.IO.Path]::GetExtension($Path) -eq '.url')
+ {
+ $URLFile = [System.IO.File]::ReadAllLines($Path) | & {
+ process
+ {
+ switch ($_)
+ {
+ { $_.StartsWith('URL=') -and $TargetPath } { "URL=$TargetPath"; break }
+ { $_.StartsWith('IconIndex=') -and ($null -ne $IconIndex) } { "IconIndex=$IconIndex"; break }
+ { $_.StartsWith('IconFile=') -and $IconLocation } { "IconFile=$IconLocation"; break }
+ default { $_; break }
+ }
+ }
+ }
+ [System.IO.File]::WriteAllLines($Path, $URLFile, [System.Text.UTF8Encoding]::new($false))
+ }
+ else
+ {
+ # Open shortcut and set initial properties.
+ $shortcut = [System.Activator]::CreateInstance([System.Type]::GetTypeFromProgID('WScript.Shell')).CreateShortcut($Path)
+ if ($TargetPath)
+ {
+ $shortcut.TargetPath = $TargetPath
+ }
+ if ($Arguments)
+ {
+ $shortcut.Arguments = $Arguments
+ }
+ if ($Description)
+ {
+ $shortcut.Description = $Description
+ }
+ if ($WorkingDirectory)
+ {
+ $shortcut.WorkingDirectory = $WorkingDirectory
+ }
+ if ($Hotkey)
+ {
+ $shortcut.Hotkey = $Hotkey
+ }
+
+ # Set the WindowStyle based on input.
+ $windowStyleInt = switch ($WindowStyle)
+ {
+ Normal { 1; break }
+ Maximized { 3; break }
+ Minimized { 7; break }
+ }
+ If ($null -ne $windowStyleInt)
+ {
+ $shortcut.WindowStyle = $WindowStyleInt
+ }
+
+ # Handle icon, starting with retrieval previous value and split the path from the index.
+ $TempIconLocation, $TempIconIndex = $shortcut.IconLocation.Split(',')
+ $newIconLocation = if ($IconLocation)
+ {
+ # New icon path was specified. Check whether new icon index was also specified.
+ if ($PSBoundParameters.ContainsKey('IconIndex'))
+ {
+ # Create new icon path from new icon path and new icon index.
+ $IconLocation + ",$IconIndex"
+ }
+ else
+ {
+ # No new icon index was specified as a parameter. We will keep the old one.
+ $IconLocation + ",$TempIconIndex"
+ }
+ }
+ elseif ($PSBoundParameters.ContainsKey('IconIndex'))
+ {
+ # New icon index was specified, but not the icon location. Append it to the icon path from the shortcut.
+ $IconLocation = $TempIconLocation + ",$IconIndex"
+ }
+ if ($newIconLocation)
+ {
+ $shortcut.IconLocation = $newIconLocation
+ }
+
+ # Save the changes.
+ $shortcut.Save()
+
+ # Set shortcut to run program as administrator.
+ if ($PSBoundParameters.ContainsKey('RunAsAdmin'))
+ {
+ $fileBytes = [System.IO.FIle]::ReadAllBytes($Path)
+ $fileBytes[21] = if ($PSBoundParameters.RunAsAdmin)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Setting shortcut to run program as administrator.'
+ $fileBytes[21] -bor 32
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Setting shortcut to not run program as administrator.'
+ $fileBytes[21] -band (-bnot 32)
+ }
+ [System.IO.FIle]::WriteAllBytes($Path, $fileBytes)
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to change the shortcut [$Path]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTBalloonTip
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTBalloonTip
+{
+ <#
+ .SYNOPSIS
+ Displays a balloon tip notification in the system tray.
+
+ .DESCRIPTION
+ Displays a balloon tip notification in the system tray. This function can be used to show notifications to the user with customizable text, title, icon, and display duration.
+
+ For Windows 10 and above, balloon tips automatically get translated by the system into toast notifications.
+
+ .PARAMETER BalloonTipText
+ Text of the balloon tip.
+
+ .PARAMETER BalloonTipIcon
+ Icon to be used. Options: 'Error', 'Info', 'None', 'Warning'.
+
+ .PARAMETER BalloonTipTime
+ Time in milliseconds to display the balloon tip. Default: 10000.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Show-ADTBalloonTip -BalloonTipText 'Installation Started' -BalloonTipTitle 'Application Name'
+
+ Displays a balloon tip with the text 'Installation Started' and the title 'Application Name'.
+
+ .EXAMPLE
+ Show-ADTBalloonTip -BalloonTipIcon 'Info' -BalloonTipText 'Installation Started' -BalloonTipTitle 'Application Name' -BalloonTipTime 1000
+
+ Displays a balloon tip with the info icon, the text 'Installation Started', the title 'Application Name', and a display duration of 1000 milliseconds.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Show-ADTBalloonTip
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'BalloonTipIcon', Justification = "This parameter is used via the function's PSBoundParameters dictionary, which is not something PSScriptAnalyzer understands. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$BalloonTipText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Windows.Forms.ToolTipIcon]$BalloonTipIcon = 'Info',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$BalloonTipTime = 10000
+ )
+
+ dynamicparam
+ {
+ # Initialize the module first if needed.
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+
+ # Define parameter dictionary for returning at the end.
+ $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
+
+ # Add in parameters we need as mandatory when there's no active ADTSession.
+ $paramDictionary.Add('BalloonTipTitle', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'BalloonTipTitle', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Title of the balloon tip.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+
+ # Return the populated dictionary.
+ return $paramDictionary
+ }
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Initalise the classic assets.
+ & $Script:CommandTable.'Initialize-ADTClassicAssets'
+
+ # Set up defaults if not specified.
+ if (!$PSBoundParameters.ContainsKey('BalloonTipTitle'))
+ {
+ $PSBoundParameters.Add('BalloonTipTitle', $adtSession.InstallTitle)
+ }
+ }
+
+ process
+ {
+ # Don't allow toast notifications with fluent dialogs unless this function was explicitly requested by the caller.
+ if (($adtConfig.UI.DialogStyle -eq 'Fluent') -and ((& $Script:CommandTable.'Get-PSCallStack')[1].Command -match '^(Show|Close)-ADTInstallationProgress$'))
+ {
+ return
+ }
+
+ try
+ {
+ try
+ {
+ # Skip balloon if in silent mode, disabled in the config or presentation is detected.
+ if (!$adtConfig.UI.BalloonNotifications)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing $($MyInvocation.MyCommand.Name) [Config Show Balloon Notifications: $($adtConfig.UI.BalloonNotifications)]. BalloonTipText: $BalloonTipText"
+ return
+ }
+ if ($adtSession -and $adtSession.IsSilent())
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing $($MyInvocation.MyCommand.Name) [Mode: $($adtSession.DeployMode)]. BalloonTipText: $BalloonTipText"
+ return
+ }
+ if (& $Script:CommandTable.'Test-ADTUserIsBusy')
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing $($MyInvocation.MyCommand.Name) [Presentation/Microphone in Use Detected: $true]. BalloonTipText: $BalloonTipText"
+ return
+ }
+
+ # Set up the balloon tip.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Displaying balloon tip notification with message [$BalloonTipText]."
+ $nabtParams = & $Script:CommandTable.'Get-ADTBoundParametersAndDefaultValues' -Invocation $MyInvocation -Exclude BalloonTipTime
+ $nabtParams.Add('Icon', $Script:Dialogs.Classic.Assets.Icon); $nabtParams.Add('Visible', $true)
+ $notifyIcon = [System.Windows.Forms.NotifyIcon]$nabtParams
+
+ # Add an event to manage disposal of the object before displaying.
+ $null = & $Script:CommandTable.'Register-ObjectEvent' -InputObject $notifyIcon -EventName BalloonTipShown -Action { $Sender.Dispose() }
+ $notifyIcon.ShowBalloonTip($BalloonTipTime)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTDialogBox
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTDialogBox
+{
+ <#
+ .SYNOPSIS
+ Display a custom dialog box with optional title, buttons, icon, and timeout.
+
+ .DESCRIPTION
+ Display a custom dialog box with optional title, buttons, icon, and timeout. The default button is "OK", the default Icon is "None", and the default Timeout is None.
+
+ Show-ADTInstallationPrompt is recommended over this function as it provides more customization and uses consistent branding with the other UI components.
+
+ .PARAMETER Text
+ Text in the message dialog box.
+
+ .PARAMETER Buttons
+ The button(s) to display on the dialog box.
+
+ .PARAMETER DefaultButton
+ The Default button that is selected. Options: First, Second, Third.
+
+ .PARAMETER Icon
+ Icon to display on the dialog box. Options: None, Stop, Question, Exclamation, Information.
+
+ .PARAMETER NotTopMost
+ Specifies whether the message box shouldn't be a system modal message box that appears in a topmost window.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.String
+
+ Returns the text of the button that was clicked.
+
+ .EXAMPLE
+ Show-ADTDialogBox -Title 'Installation Notice' -Text 'Installation will take approximately 30 minutes. Do you wish to proceed?' -Buttons 'OKCancel' -DefaultButton 'Second' -Icon 'Exclamation' -Timeout 600 -Topmost $false
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Show-ADTDialogBox
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Enter a message for the dialog box.')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Text,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('OK', 'OKCancel', 'AbortRetryIgnore', 'YesNoCancel', 'YesNo', 'RetryCancel', 'CancelTryAgainContinue')]
+ [System.String]$Buttons = 'OK',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('First', 'Second', 'Third')]
+ [System.String]$DefaultButton = 'First',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Exclamation', 'Information', 'None', 'Stop', 'Question')]
+ [System.String]$Icon = 'None',
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost
+ )
+
+ dynamicparam
+ {
+ # Initialize the module if there's no session and it hasn't been previously initialized.
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Define parameter dictionary for returning at the end.
+ $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
+
+ # Add in parameters we need as mandatory when there's no active ADTSession.
+ $paramDictionary.Add('Title', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Title', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Title of the message dialog box.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('Timeout', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Timeout', [System.UInt32], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = $false; HelpMessage = 'Specifies how long, in seconds, to show the message prompt before aborting.' }
+ [System.Management.Automation.ValidateScriptAttribute]::new({
+ if ($_ -gt $adtConfig.UI.DefaultTimeout)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Timeout -ProvidedValue $_ -ExceptionMessage 'The installation UI dialog timeout cannot be longer than the timeout specified in the config.psd1 file.'))
+ }
+ return !!$_
+ })
+ )
+ ))
+
+ # Return the populated dictionary.
+ return $paramDictionary
+ }
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Set up defaults if not specified.
+ $Title = if (!$PSBoundParameters.ContainsKey('Title'))
+ {
+ $adtSession.InstallTitle
+ }
+ else
+ {
+ $PSBoundParameters.Title
+ }
+ $Timeout = if (!$PSBoundParameters.ContainsKey('Timeout'))
+ {
+ $adtConfig.UI.DefaultTimeout
+ }
+ else
+ {
+ $PSBoundParameters.Timeout
+ }
+ }
+
+ process
+ {
+ # Bypass if in silent mode.
+ if ($adtSession -and $adtSession.IsNonInteractive())
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing $($MyInvocation.MyCommand.Name) [Mode: $($adtSession.deployMode)]. Text: $Text"
+ return
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Displaying Dialog Box with message: $Text..."
+ try
+ {
+ try
+ {
+ $result = switch ((& $Script:CommandTable.'Get-ADTEnvironmentTable').Shell.Popup($Text, $Timeout, $Title, ($Script:Dialogs.Box.Buttons.$Buttons + $Script:Dialogs.Box.Icons.$Icon + $Script:Dialogs.Box.DefaultButtons.$DefaultButton + (4096 * !$NotTopMost))))
+ {
+ 1 { 'OK'; break }
+ 2 { 'Cancel'; break }
+ 3 { 'Abort'; break }
+ 4 { 'Retry'; break }
+ 5 { 'Ignore'; break }
+ 6 { 'Yes'; break }
+ 7 { 'No'; break }
+ 10 { 'Try Again'; break }
+ 11 { 'Continue'; break }
+ -1 { 'Timeout'; break }
+ default { 'Unknown'; break }
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Dialog Box Response: $result"
+ return $result
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTHelpConsole
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTHelpConsole
+{
+ <#
+ .SYNOPSIS
+ Displays a help console for the ADT module.
+
+ .DESCRIPTION
+ Displays a help console for the ADT module in a new PowerShell window. The console provides a graphical interface to browse and view detailed help information for all commands exported by the ADT module. The help console includes a list box to select commands and a text box to display the full help content for the selected command.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Show-ADTHelpConsole
+
+ Opens a new PowerShell window displaying the help console for the ADT module.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Show-ADTHelpConsole
+ #>
+
+ # Run this via a new PowerShell window so it doesn't stall the main thread.
+ & $Script:CommandTable.'Start-Process' -FilePath (& $Script:CommandTable.'Get-ADTPowerShellProcessPath') -NoNewWindow -ArgumentList "$(if (!(& $Script:CommandTable.'Test-ADTModuleIsReleaseBuild')) { "-ExecutionPolicy Bypass " })-NonInteractive -NoProfile -NoLogo -EncodedCommand $(& $Script:CommandTable.'Out-ADTPowerShellEncodedCommand' -Command "& {$($Script:CommandTable.'Show-ADTHelpConsoleInternal'.ScriptBlock)} -ModuleName '$($Script:PSScriptRoot)\$($MyInvocation.MyCommand.Module.Name).psd1' -Guid $($MyInvocation.MyCommand.Module.Guid) -ModuleVersion $($MyInvocation.MyCommand.Module.Version)")"
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationProgress
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationProgress
+{
+ <#
+ .SYNOPSIS
+ Displays a progress dialog in a separate thread with an updateable custom message.
+
+ .DESCRIPTION
+ Creates a WPF window in a separate thread to display a marquee style progress ellipse with a custom message that can be updated. The status message supports line breaks.
+
+ The first time this function is called in a script, it will display a balloon tip notification to indicate that the installation has started (provided balloon tips are enabled in the config.psd1 file).
+
+ .PARAMETER WindowLocation
+ The location of the progress window. Default: center of the screen.
+
+ .PARAMETER MessageAlignment
+ The text alignment to use for the status message. Default: center.
+
+ .PARAMETER NotTopMost
+ Specifies whether the progress window shouldn't be topmost. Default: $false.
+
+ .PARAMETER NoRelocation
+ Specifies whether to not reposition the window upon updating the message. Default: $false.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Show-ADTInstallationProgress
+
+ Uses the default status message from the strings.psd1 file.
+
+ .EXAMPLE
+ Show-ADTInstallationProgress -StatusMessage 'Installation in Progress...'
+
+ Displays a progress dialog with the status message 'Installation in Progress...'.
+
+ .EXAMPLE
+ Show-ADTInstallationProgress -StatusMessage "Installation in Progress...`nThe installation may take 20 minutes to complete."
+
+ Displays a progress dialog with a multiline status message.
+
+ .EXAMPLE
+ Show-ADTInstallationProgress -StatusMessage 'Installation in Progress...' -WindowLocation 'BottomRight' -NotTopMost
+
+ Displays a progress dialog with the status message 'Installation in Progress...', positioned at the bottom right of the screen, and not set as topmost.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Show-ADTInstallationProgress
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Default', 'TopLeft', 'Top', 'TopRight', 'TopCenter', 'BottomLeft', 'Bottom', 'BottomRight')]
+ [System.String]$WindowLocation = 'Default',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Windows.TextAlignment]$MessageAlignment,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoRelocation
+ )
+
+ dynamicparam
+ {
+ # Initialize the module first if needed.
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Define parameter dictionary for returning at the end.
+ $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
+
+ # Add in parameters we need as mandatory when there's no active ADTSession.
+ $paramDictionary.Add('WindowTitle', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'WindowTitle', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'The title of the window to be displayed. The default is the derived value from "$($adtSession.InstallTitle)".' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('WindowSubtitle', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'WindowSubtitle', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession -and ($adtConfig.UI.DialogStyle -eq 'Fluent'); HelpMessage = 'The subtitle of the window to be displayed with a fluent progress window. The default is the derived value from "$($adtSession.DeploymentType)".' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('StatusMessage', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'StatusMessage', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'The status message to be displayed. The default status message is taken from the config.psd1 file.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('StatusMessageDetail', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'StatusMessageDetail', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession -and ($adtConfig.UI.DialogStyle -eq 'Fluent'); HelpMessage = 'The status message detail to be displayed with a fluent progress window. The default status message is taken from the config.psd1 file.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+
+ # Return the populated dictionary.
+ return $paramDictionary
+ }
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+ $errRecord = $null
+
+ # Set up defaults if not specified.
+ if (!$PSBoundParameters.ContainsKey('WindowTitle'))
+ {
+ $PSBoundParameters.Add('WindowTitle', $adtSession.InstallTitle)
+ }
+ if (!$PSBoundParameters.ContainsKey('WindowSubtitle'))
+ {
+ $PSBoundParameters.Add('WindowSubtitle', [System.String]::Format($adtStrings.WelcomePrompt.Fluent.Subtitle, $adtSession.GetDeploymentTypeName()))
+ }
+ if (!$PSBoundParameters.ContainsKey('StatusMessage'))
+ {
+ $PSBoundParameters.Add('StatusMessage', $adtStrings.Progress."Message$($adtSession.DeploymentType)")
+ }
+ if (!$PSBoundParameters.ContainsKey('StatusMessageDetail') -and ($adtConfig.UI.DialogStyle -eq 'Fluent'))
+ {
+ $PSBoundParameters.Add('StatusMessageDetail', $adtStrings.Progress."Message$($adtSession.DeploymentType)Detail")
+ }
+ }
+
+ process
+ {
+ # Determine if progress window is open before proceeding.
+ $progressOpen = & $Script:CommandTable.'Test-ADTInstallationProgressRunning'
+
+ # Return early in silent mode.
+ if ($adtSession)
+ {
+ if ($adtSession.IsSilent())
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing $($MyInvocation.MyCommand.Name) [Mode: $($adtSession.DeployMode)]. Status message: $($PSBoundParameters.StatusMessage)"
+ return
+ }
+
+ # Notify user that the software installation has started.
+ if (!$progressOpen)
+ {
+ try
+ {
+ & $Script:CommandTable.'Show-ADTBalloonTip' -BalloonTipIcon Info -BalloonTipText "$($adtSession.GetDeploymentTypeName()) $($adtStrings.BalloonText.Start)"
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+ }
+
+ # Call the underlying function to open the progress window.
+ try
+ {
+ try
+ {
+ # Perform the dialog action.
+ if (!$progressOpen)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Creating the progress dialog in a separate thread with message: [$($PSBoundParameters.StatusMessage)]."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Updating the progress dialog with message: [$($PSBoundParameters.StatusMessage)]."
+ }
+ & $Script:CommandTable."$($MyInvocation.MyCommand.Name)$($adtConfig.UI.DialogStyle)" @PSBoundParameters
+
+ # Add a callback to close it if we've opened for the first time.
+ if (!(& $Script:CommandTable.'Test-ADTInstallationProgressRunning').Equals($progressOpen))
+ {
+ & $Script:CommandTable.'Add-ADTSessionFinishingCallback' -Callback $Script:CommandTable.'Close-ADTInstallationProgress'
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord ($errRecord = $_)
+ }
+ finally
+ {
+ if ($errRecord)
+ {
+ & $Script:CommandTable.'Close-ADTInstallationProgress'
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationPrompt
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationPrompt
+{
+ <#
+ .SYNOPSIS
+ Displays a custom installation prompt with the toolkit branding and optional buttons.
+
+ .DESCRIPTION
+ Displays a custom installation prompt with the toolkit branding and optional buttons. Any combination of Left, Middle, or Right buttons can be displayed. The return value of the button clicked by the user is the button text specified. The prompt can also display a system icon and be configured to persist, minimize other windows, or timeout after a specified period.
+
+ .PARAMETER Message
+ The message text to be displayed on the prompt.
+
+ .PARAMETER MessageAlignment
+ Alignment of the message text. Options: Left, Center, Right. Default: Center.
+
+ .PARAMETER ButtonLeftText
+ Show a button on the left of the prompt with the specified text.
+
+ .PARAMETER ButtonRightText
+ Show a button on the right of the prompt with the specified text.
+
+ .PARAMETER ButtonMiddleText
+ Show a button in the middle of the prompt with the specified text.
+
+ .PARAMETER Icon
+ Show a system icon in the prompt. Options: Application, Asterisk, Error, Exclamation, Hand, Information, None, Question, Shield, Warning, WinLogo. Default: None.
+
+ .PARAMETER NoWait
+ Presents the dialog in a separate, independent thread so that the main process isn't stalled waiting for a response.
+
+ .PARAMETER PersistPrompt
+ Specify whether to make the prompt persist in the center of the screen every couple of seconds, specified in the config.psd1 file. The user will have no option but to respond to the prompt.
+
+ .PARAMETER MinimizeWindows
+ Specifies whether to minimize other windows when displaying prompt.
+
+ .PARAMETER NoExitOnTimeout
+ Specifies whether to not exit the script if the UI times out.
+
+ .PARAMETER NotTopMost
+ Specifies whether the prompt shouldn't be topmost, above all other windows.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Show-ADTInstallationPrompt -Message 'Do you want to proceed with the installation?' -ButtonRightText 'Yes' -ButtonLeftText 'No'
+
+ .EXAMPLE
+ Show-ADTInstallationPrompt -Title 'Funny Prompt' -Message 'How are you feeling today?' -ButtonRightText 'Good' -ButtonLeftText 'Bad' -ButtonMiddleText 'Indifferent'
+
+ .EXAMPLE
+ Show-ADTInstallationPrompt -Message 'You can customize text to appear at the end of an install, or remove it completely for unattended installations.' -ButtonRightText 'OK' -Icon Information -NoWait
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Show-ADTInstallationPrompt
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Message,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Left', 'Center', 'Right')]
+ [System.String]$MessageAlignment = 'Center',
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonRightText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonLeftText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ButtonMiddleText,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Application', 'Asterisk', 'Error', 'Exclamation', 'Hand', 'Information', 'Question', 'Shield', 'Warning', 'WinLogo')]
+ [System.String]$Icon,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PersistPrompt,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$MinimizeWindows,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoExitOnTimeout,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost
+ )
+
+ dynamicparam
+ {
+ # Initialize variables.
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Define parameter dictionary for returning at the end.
+ $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
+
+ # Add in parameters we need as mandatory when there's no active ADTSession.
+ $paramDictionary.Add('Title', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Title', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Title of the prompt. Default: the application installation name.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('Subtitle', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Subtitle', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Subtitle of the prompt. Default: the application deployment type.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('Timeout', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Timeout', [System.UInt32], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = $false; HelpMessage = 'Specifies how long, in seconds, to show the message prompt before aborting.' }
+ [System.Management.Automation.ValidateScriptAttribute]::new({
+ if ($_ -gt $adtConfig.UI.DefaultTimeout)
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Timeout -ProvidedValue $_ -ExceptionMessage 'The installation UI dialog timeout cannot be longer than the timeout specified in the config.psd1 file.'))
+ }
+ return !!$_
+ })
+ )
+ ))
+
+ # Return the populated dictionary.
+ return $paramDictionary
+ }
+
+ begin
+ {
+ # Throw a terminating error if at least one button isn't specified.
+ if (!($PSBoundParameters.Keys -match '^Button'))
+ {
+ $naerParams = @{
+ Exception = [System.ArgumentException]::new('At least one button must be specified when calling this function.')
+ Category = [System.Management.Automation.ErrorCategory]::InvalidArgument
+ ErrorId = 'MandatoryParameterMissing'
+ TargetObject = $PSBoundParameters
+ RecommendedAction = "Please review the supplied parameters used against $($MyInvocation.MyCommand.Name) and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Set up defaults if not specified.
+ if (!$PSBoundParameters.ContainsKey('Title'))
+ {
+ $PSBoundParameters.Add('Title', $adtSession.InstallTitle)
+ }
+ if (!$PSBoundParameters.ContainsKey('Subtitle'))
+ {
+ $PSBoundParameters.Add('Subtitle', [System.String]::Format((& $Script:CommandTable.'Get-ADTStringTable').WelcomePrompt.Fluent.Subtitle, $adtSession.GetDeploymentTypeName()))
+ }
+ if (!$PSBoundParameters.ContainsKey('Timeout'))
+ {
+ $PSBoundParameters.Add('Timeout', $adtConfig.UI.DefaultTimeout)
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Bypass if in non-interactive mode.
+ if ($adtSession -and $adtSession.IsNonInteractive())
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing $($MyInvocation.MyCommand.Name) [Mode: $($adtSession.DeployMode)]. Message: $Message"
+ return
+ }
+
+ # Resolve the bound parameters to a string.
+ $paramsString = [PSADT.Shared.Utility]::ConvertDictToPowerShellArgs($PSBoundParameters)
+
+ # If the NoWait parameter is specified, launch a new PowerShell session to show the prompt asynchronously.
+ if ($NoWait)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Displaying custom installation prompt asynchronously with the parameters: [$($paramsString.Replace("''", "'"))]."
+ & $Script:CommandTable.'Start-Process' -FilePath (& $Script:CommandTable.'Get-ADTPowerShellProcessPath') -ArgumentList "$(if (!(& $Script:CommandTable.'Test-ADTModuleIsReleaseBuild')) { "-ExecutionPolicy Bypass " })-NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command & (Import-Module -FullyQualifiedName @{ ModuleName = '$("$($Script:PSScriptRoot)\$($MyInvocation.MyCommand.Module.Name).psd1".Replace("'", "''"))'; Guid = '$($MyInvocation.MyCommand.Module.Guid)'; ModuleVersion = '$($MyInvocation.MyCommand.Module.Version)' } -PassThru) { & `$CommandTable.'Initialize-ADTModule' -ScriptDirectory '$([System.String]::Join("', '", $Script:ADT.Directories.Script.Replace("'", "''")))'; `$null = & `$CommandTable.'$($MyInvocation.MyCommand.Name)$($adtConfig.UI.DialogStyle)' $($paramsString.Replace('"', '\"')) }" -WindowStyle Hidden -ErrorAction Ignore
+ return
+ }
+
+ # Close the Installation Progress dialog if running.
+ if ($adtSession)
+ {
+ & $Script:CommandTable.'Close-ADTInstallationProgress'
+ }
+
+ # Call the underlying function to open the message prompt.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Displaying custom installation prompt with the parameters: [$($paramsString.Replace("''", "'"))]."
+ return & $Script:CommandTable."$($MyInvocation.MyCommand.Name)$($adtConfig.UI.DialogStyle)" @PSBoundParameters
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationRestartPrompt
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationRestartPrompt
+{
+ <#
+ .SYNOPSIS
+ Displays a restart prompt with a countdown to a forced restart.
+
+ .DESCRIPTION
+ Displays a restart prompt with a countdown to a forced restart. The prompt can be customized with a title, countdown duration, and whether it should be topmost. It also supports silent mode where the restart can be triggered without user interaction.
+
+ .PARAMETER CountdownSeconds
+ Specifies the number of seconds to display the restart prompt. Default: 60
+
+ .PARAMETER CountdownNoHideSeconds
+ Specifies the number of seconds to display the restart prompt without allowing the window to be hidden. Default: 30
+
+ .PARAMETER SilentCountdownSeconds
+ Specifies number of seconds to countdown for the restart when the toolkit is running in silent mode and `-SilentRestart` isn't specified. Default: 5
+
+ .PARAMETER SilentRestart
+ Specifies whether the restart should be triggered when DeployMode is silent or very silent.
+
+ .PARAMETER NoCountdown
+ Specifies whether the user should receive a prompt to immediately restart their workstation.
+
+ .PARAMETER NotTopMost
+ Specifies whether the prompt shouldn't be topmost, above all other windows.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Show-ADTInstallationRestartPrompt -NoCountdown
+
+ Displays a restart prompt without a countdown.
+
+ .EXAMPLE
+ Show-ADTInstallationRestartPrompt -CountdownSeconds 300
+
+ Displays a restart prompt with a 300-second countdown.
+
+ .EXAMPLE
+ Show-ADTInstallationRestartPrompt -CountdownSeconds 600 -CountdownNoHideSeconds 60
+
+ Displays a restart prompt with a 600-second countdown and triggers a silent restart with a 60-second countdown in silent mode.
+
+ .NOTES
+ Be mindful of the countdown you specify for the reboot as code directly after this function might NOT be able to execute - that includes logging.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Show-ADTInstallationRestartPrompt
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'Countdown')]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'NoCountdown')]
+ [System.Management.Automation.SwitchParameter]$NoCountdown,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Countdown')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$CountdownSeconds = 60,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Countdown')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$CountdownNoHideSeconds = 30,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SilentRestart')]
+ [System.Management.Automation.SwitchParameter]$SilentRestart,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'SilentRestart')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$SilentCountdownSeconds = 5,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NotTopMost
+ )
+
+ dynamicparam
+ {
+ # Initialize variables.
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+
+ # Define parameter dictionary for returning at the end.
+ $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
+
+ # Add in parameters we need as mandatory when there's no active ADTSession.
+ $paramDictionary.Add('Title', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Title', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Title of the prompt. Default: the application installation name.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('Subtitle', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Subtitle', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = 'Subtitle of the prompt. Default: the application deployment type.' }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+
+ # Return the populated dictionary.
+ return $paramDictionary
+ }
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Set up defaults if not specified.
+ if (!$PSBoundParameters.ContainsKey('Title'))
+ {
+ $PSBoundParameters.Add('Title', $adtSession.InstallTitle)
+ }
+ if (!$PSBoundParameters.ContainsKey('Subtitle'))
+ {
+ $PSBoundParameters.Add('Subtitle', [System.String]::Format($adtStrings.WelcomePrompt.Fluent.Subtitle, $adtSession.GetDeploymentTypeName()))
+ }
+ if (!$PSBoundParameters.ContainsKey('CountdownSeconds'))
+ {
+ $PSBoundParameters.Add('CountdownSeconds', $CountdownSeconds)
+ }
+ if (!$PSBoundParameters.ContainsKey('CountdownNoHideSeconds'))
+ {
+ $PSBoundParameters.Add('CountdownNoHideSeconds', $CountdownNoHideSeconds)
+ }
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If in non-interactive mode.
+ if ($adtSession -and $adtSession.IsSilent())
+ {
+ if ($SilentRestart)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Triggering restart silently, because the deploy mode is set to [$($adtSession.DeployMode)] and [-SilentRestart] has been specified. Timeout is set to [$SilentCountdownSeconds] seconds."
+ & $Script:CommandTable.'Start-Process' -FilePath (& $Script:CommandTable.'Get-ADTPowerShellProcessPath') -ArgumentList "-NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command Start-Sleep -Seconds $SilentCountdownSeconds; Restart-Computer -Force" -WindowStyle Hidden -ErrorAction Ignore
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Skipping restart, because the deploy mode is set to [$($adtSession.DeployMode)] and [-SilentRestart] was not specified."
+ }
+ return
+ }
+
+ # Check if we are already displaying a restart prompt.
+ if (& $Script:CommandTable.'Get-Process' | & { process { if ($_.MainWindowTitle -match $adtStrings.RestartPrompt.Title) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$($MyInvocation.MyCommand.Name) was invoked, but an existing restart prompt was detected. Cancelling restart prompt." -Severity 2
+ return
+ }
+
+ # If the script has been dot-source invoked by the deploy app script, display the restart prompt asynchronously.
+ if ($adtSession)
+ {
+ if ($NoCountdown)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invoking $($MyInvocation.MyCommand.Name) asynchronously with no countdown..."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invoking $($MyInvocation.MyCommand.Name) asynchronously with a [$CountdownSeconds] second countdown..."
+ }
+
+ # Start another powershell instance silently with function parameters from this function.
+ & $Script:CommandTable.'Start-Process' -FilePath (& $Script:CommandTable.'Get-ADTPowerShellProcessPath') -ArgumentList "$(if (!(& $Script:CommandTable.'Test-ADTModuleIsReleaseBuild')) { "-ExecutionPolicy Bypass " })-NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command & (Import-Module -FullyQualifiedName @{ ModuleName = '$("$($Script:PSScriptRoot)\$($MyInvocation.MyCommand.Module.Name).psd1".Replace("'", "''"))'; Guid = '$($MyInvocation.MyCommand.Module.Guid)'; ModuleVersion = '$($MyInvocation.MyCommand.Module.Version)' } -PassThru) { & `$CommandTable.'Initialize-ADTModule' -ScriptDirectory '$([System.String]::Join("', '", $Script:ADT.Directories.Script.Replace("'", "''")))'; `$null = & `$CommandTable.'$($MyInvocation.MyCommand.Name)$($adtConfig.UI.DialogStyle)' $([PSADT.Shared.Utility]::ConvertDictToPowerShellArgs($PSBoundParameters, ('SilentRestart', 'SilentCountdownSeconds')).Replace('"', '\"')) }" -WindowStyle Hidden -ErrorAction Ignore
+ return
+ }
+
+ # Call the underlying function to open the restart prompt.
+ return & $Script:CommandTable."$($MyInvocation.MyCommand.Name)$($adtConfig.UI.DialogStyle)" @PSBoundParameters
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Show-ADTInstallationWelcome
+#
+#-----------------------------------------------------------------------------
+
+function Show-ADTInstallationWelcome
+{
+ <#
+ .SYNOPSIS
+ Show a welcome dialog prompting the user with information about the deployment and actions to be performed before the deployment can begin.
+
+ .DESCRIPTION
+ The following prompts can be included in the welcome dialog:
+
+ * Close the specified running applications, or optionally close the applications without showing a prompt (using the `-Silent` switch).
+ * Defer the deployment a certain number of times, for a certain number of days or until a deadline is reached.
+ * Countdown until applications are automatically closed.
+ * Prevent users from launching the specified applications while the deployment is in progress.
+
+ .PARAMETER CloseProcesses
+ Name of the process to stop (do not include the .exe). Specify multiple processes separated by a comma. Specify custom descriptions like this: @{ Name = 'winword'; Description = 'Microsoft Office Word'},@{ Name = 'excel'; Description = 'Microsoft Office Excel'}
+
+ .PARAMETER Silent
+ Stop processes without prompting the user.
+
+ .PARAMETER CloseProcessesCountdown
+ Option to provide a countdown in seconds until the specified applications are automatically closed. This only takes effect if deferral is not allowed or has expired.
+
+ .PARAMETER ForceCloseProcessesCountdown
+ Option to provide a countdown in seconds until the specified applications are automatically closed regardless of whether deferral is allowed.
+
+ .PARAMETER PromptToSave
+ Specify whether to prompt to save working documents when the user chooses to close applications by selecting the "Close Programs" button. Option does not work in SYSTEM context unless toolkit launched with "psexec.exe -s -i" to run it as an interactive process under the SYSTEM account.
+
+ .PARAMETER PersistPrompt
+ Specify whether to make the Show-ADTInstallationWelcome prompt persist in the center of the screen every couple of seconds, specified in the config.psd1. The user will have no option but to respond to the prompt. This only takes effect if deferral is not allowed or has expired.
+
+ .PARAMETER BlockExecution
+ Option to prevent the user from launching processes/applications, specified in -CloseProcesses, during the deployment.
+
+ .PARAMETER AllowDefer
+ Enables an optional defer button to allow the user to defer the deployment.
+
+ .PARAMETER AllowDeferCloseProcesses
+ Enables an optional defer button to allow the user to defer the deployment only if there are running applications that need to be closed. This parameter automatically enables -AllowDefer
+
+ .PARAMETER DeferTimes
+ Specify the number of times the deployment can be deferred.
+
+ .PARAMETER DeferDays
+ Specify the number of days since first run that the deployment can be deferred. This is converted to a deadline.
+
+ .PARAMETER DeferDeadline
+ Specify the deadline date until which the deployment can be deferred.
+
+ Specify the date in the local culture if the script is intended for that same culture.
+
+ If the script is intended to run on EN-US machines, specify the date in the format: "08/25/2013" or "08-25-2013" or "08-25-2013 18:00:00"
+
+ If the script is intended for multiple cultures, specify the date in the universal sortable date/time format: "2013-08-22 11:51:52Z"
+
+ The deadline date will be displayed to the user in the format of their culture.
+
+ .PARAMETER CheckDiskSpace
+ Specify whether to check if there is enough disk space for the deployment to proceed.
+
+ If this parameter is specified without the RequiredDiskSpace parameter, the required disk space is calculated automatically based on the size of the script source and associated files.
+
+ .PARAMETER RequiredDiskSpace
+ Specify required disk space in MB, used in combination with CheckDiskSpace.
+
+ .PARAMETER NoMinimizeWindows
+ Specifies whether to minimize other windows when displaying prompt. Default: $false.
+
+ .PARAMETER TopMost
+ Specifies whether the windows is the topmost window. Default: $true.
+
+ .PARAMETER ForceCountdown
+ Specify a countdown to display before automatically proceeding with the deployment when a deferral is enabled.
+
+ .PARAMETER CustomText
+ Specify whether to display a custom message specified in the string.psd1 file. Custom message must be populated for each language section in the string.psd1 file.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ Show-ADTInstallationWelcome -CloseProcesses iexplore, winword, excel
+
+ Prompt the user to close Internet Explorer, Word and Excel.
+
+ .EXAMPLE
+ Show-ADTInstallationWelcome -CloseProcesses @{ Name = 'winword' }, @{ Name = 'excel' } -Silent
+
+ Close Word and Excel without prompting the user.
+
+ .EXAMPLE
+ Show-ADTInstallationWelcome -CloseProcesses @{ Name = 'winword' }, @{ Name = 'excel' } -BlockExecution
+
+ Close Word and Excel and prevent the user from launching the applications while the deployment is in progress.
+
+ .EXAMPLE
+ Show-ADTInstallationWelcome -CloseProcesses @{ Name = 'winword'; Description = 'Microsoft Office Word' }, @{ Name = 'excel'; Description = 'Microsoft Office Excel' } -CloseProcessesCountdown 600
+
+ Prompt the user to close Word and Excel, with customized descriptions for the applications and automatically close the applications after 10 minutes.
+
+ .EXAMPLE
+ Show-ADTInstallationWelcome -CloseProcesses @{ Name = 'winword' }, @{ Name = 'msaccess' }, @{ Name = 'excel' } -PersistPrompt
+
+ Prompt the user to close Word, MSAccess and Excel. By using the PersistPrompt switch, the dialog will return to the center of the screen every couple of seconds, specified in the config.psd1, so the user cannot ignore it by dragging it aside.
+
+ .EXAMPLE
+ Show-ADTInstallationWelcome -AllowDefer -DeferDeadline '25/08/2013'
+
+ Allow the user to defer the deployment until the deadline is reached.
+
+ .EXAMPLE
+ Show-ADTInstallationWelcome -CloseProcesses @{ Name = 'winword' }, @{ Name = 'excel' } -BlockExecution -AllowDefer -DeferTimes 10 -DeferDeadline '25/08/2013' -CloseProcessesCountdown 600
+
+ Close Word and Excel and prevent the user from launching the applications while the deployment is in progress.
+
+ Allow the user to defer the deployment a maximum of 10 times or until the deadline is reached, whichever happens first.
+
+ When deferral expires, prompt the user to close the applications and automatically close them after 10 minutes.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ The process descriptions are retrieved via Get-Process, with a fall back on the process name if no description is available. Alternatively, you can specify the description yourself with a '=' symbol - see examples.
+
+ The dialog box will timeout after the timeout specified in the config.psd1 file (default 55 minutes) to prevent Intune/SCCM deployments from timing out and returning a failure code. When the dialog times out, the script will exit and return a 1618 code (SCCM fast retry code).
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Show-ADTInstallationWelcome
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'None')]
+ param
+ (
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify process names and an optional process description, e.g. @{ Name = "winword"; Description = "Microsoft Word"}')]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Types.ProcessObject[]]$CloseProcesses,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify whether to prompt user or force close the applications.')]
+ [System.Management.Automation.SwitchParameter]$Silent,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify a countdown to display before automatically closing applications where deferral is not allowed or has expired.')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$CloseProcessesCountdown,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify a countdown to display before automatically closing applications whether or not deferral is allowed.')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$ForceCloseProcessesCountdown,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify whether to prompt to save working documents when the user chooses to close applications by selecting the "Close Programs" button.')]
+ [System.Management.Automation.SwitchParameter]$PromptToSave,
+
+ [Parameter(Mandatory = $false, HelpMessage = ' Specify whether to make the prompt persist in the center of the screen every couple of seconds, specified in the config.psd1 file.')]
+ [System.Management.Automation.SwitchParameter]$PersistPrompt,
+
+ [Parameter(Mandatory = $false, HelpMessage = ' Specify whether to block execution of the processes during deployment.')]
+ [System.Management.Automation.SwitchParameter]$BlockExecution,
+
+ [Parameter(Mandatory = $false, HelpMessage = ' Specify whether to enable the optional defer button on the dialog box.')]
+ [System.Management.Automation.SwitchParameter]$AllowDefer,
+
+ [Parameter(Mandatory = $false, HelpMessage = ' Specify whether to enable the optional defer button on the dialog box only if an app needs to be closed.')]
+ [System.Management.Automation.SwitchParameter]$AllowDeferCloseProcesses,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify the number of times the deferral is allowed.')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$DeferTimes,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify the number of days since first run that the deferral is allowed.')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$DeferDays,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify the deadline (in format dd/mm/yyyy) for which deferral will expire as an option.')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$DeferDeadline,
+
+ [Parameter(Mandatory = $true, HelpMessage = 'Specify whether to check if there is enough disk space for the deployment to proceed. If this parameter is specified without the RequiredDiskSpace parameter, the required disk space is calculated automatically based on the size of the script source and associated files.', ParameterSetName = 'CheckDiskSpace')]
+ [System.Management.Automation.SwitchParameter]$CheckDiskSpace,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify required disk space in MB, used in combination with $CheckDiskSpace.', ParameterSetName = 'CheckDiskSpace')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$RequiredDiskSpace,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify whether to minimize other windows when displaying prompt.')]
+ [System.Management.Automation.SwitchParameter]$NoMinimizeWindows,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specifies whether the window is the topmost window.')]
+ [System.Management.Automation.SwitchParameter]$NotTopMost,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify a countdown to display before automatically proceeding with the deployment when a deferral is enabled.')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$ForceCountdown,
+
+ [Parameter(Mandatory = $false, HelpMessage = 'Specify whether to display a custom message specified in the string.psd1 file. Custom message must be populated for each language section in the string.psd1 file.')]
+ [System.Management.Automation.SwitchParameter]$CustomText
+ )
+
+ dynamicparam
+ {
+ # Initialize variables.
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ $adtStrings = & $Script:CommandTable.'Get-ADTStringTable'
+ $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+
+ # Define parameter dictionary for returning at the end.
+ $paramDictionary = [System.Management.Automation.RuntimeDefinedParameterDictionary]::new()
+
+ # Add in parameters we need as mandatory when there's no active ADTSession.
+ $paramDictionary.Add('Title', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Title', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = "Title of the prompt. Default: the application deployment name." }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('Subtitle', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'Subtitle', [System.String], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession -and ($adtConfig.UI.DialogStyle -eq 'Fluent'); HelpMessage = "Subtitle of the prompt. Default: the application deployment type." }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+ $paramDictionary.Add('DeploymentType', [System.Management.Automation.RuntimeDefinedParameter]::new(
+ 'DeploymentType', [PSADT.Module.DeploymentType], $(
+ [System.Management.Automation.ParameterAttribute]@{ Mandatory = !$adtSession; HelpMessage = "The deployment type. Default: the session's DeploymentType value." }
+ [System.Management.Automation.ValidateNotNullOrEmptyAttribute]::new()
+ )
+ ))
+
+ # Return the populated dictionary.
+ return $paramDictionary
+ }
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $adtEnv = & $Script:CommandTable.'Get-ADTEnvironmentTable'
+
+ # Set up defaults if not specified.
+ if (!$PSBoundParameters.ContainsKey('DeploymentType'))
+ {
+ $PSBoundParameters.Add('DeploymentType', $adtSession.DeploymentType)
+ }
+ if (!$PSBoundParameters.ContainsKey('Title'))
+ {
+ $PSBoundParameters.Add('Title', $adtSession.InstallTitle)
+ }
+ if (!$PSBoundParameters.ContainsKey('Subtitle'))
+ {
+ $PSBoundParameters.Add('Subtitle', [System.String]::Format($adtStrings.WelcomePrompt.Fluent.Subtitle, $adtSession.GetDeploymentTypeName()))
+ }
+
+ # Instantiate new object to hold all data needed within this call.
+ $welcomeState = [PSADT.Types.WelcomeState]::new()
+ $deferDeadlineUniversal = $null
+ $promptResult = $null
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If running in NonInteractive mode, force the processes to close silently.
+ if (!$PSBoundParameters.ContainsKey('Silent') -and $adtSession -and ($adtSession.IsNonInteractive() -or $adtSession.IsSilent()))
+ {
+ $Silent = $true
+ }
+
+ # If using Zero-Config MSI Deployment, append any executables found in the MSI to the CloseProcesses list
+ if ($adtSession -and ($msiExecutables = $adtSession.GetDefaultMsiExecutablesList()))
+ {
+ $CloseProcesses = $(if ($CloseProcesses) { $CloseProcesses }; $msiExecutables)
+ }
+
+ # Check disk space requirements if specified
+ if ($adtSession -and $CheckDiskSpace -and ($scriptDir = try { & $Script:CommandTable.'Get-ADTSessionCacheScriptDirectory' } catch { $null = $null }))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Evaluating disk space requirements.'
+ if (!$RequiredDiskSpace)
+ {
+ try
+ {
+ # Determine the size of the Files folder
+ $fso = & $Script:CommandTable.'New-Object' -ComObject Scripting.FileSystemObject
+ $RequiredDiskSpace = [System.Math]::Round($fso.GetFolder($scriptDir).Size / 1MB)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to calculate disk space requirement from source files.`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 3
+ }
+ finally
+ {
+ $null = try
+ {
+ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($fso)
+ }
+ catch
+ {
+ $null
+ }
+ }
+ }
+ if (($freeDiskSpace = & $Script:CommandTable.'Get-ADTFreeDiskSpace') -lt $RequiredDiskSpace)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to meet minimum disk space requirement. Space Required [$RequiredDiskSpace MB], Space Available [$freeDiskSpace MB]." -Severity 3
+ if (!$Silent)
+ {
+ & $Script:CommandTable.'Show-ADTInstallationPrompt' -Message ((& $Script:CommandTable.'Get-ADTStringTable').DiskSpace.Message -f $PSBoundParameters.Title, $RequiredDiskSpace, $freeDiskSpace) -ButtonRightText OK -Icon Error
+ }
+ & $Script:CommandTable.'Close-ADTSession' -ExitCode $adtConfig.UI.DefaultExitCode
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Successfully passed minimum disk space requirement check.'
+ }
+
+ # Check Deferral history and calculate remaining deferrals.
+ if ($AllowDefer -or $AllowDeferCloseProcesses)
+ {
+ # Set $AllowDefer to true if $AllowDeferCloseProcesses is true.
+ $AllowDefer = $true
+
+ # Get the deferral history from the registry.
+ $deferHistory = if ($adtSession) { & $Script:CommandTable.'Get-ADTDeferHistory' }
+ $deferHistoryTimes = $deferHistory | & $Script:CommandTable.'Select-Object' -ExpandProperty DeferTimesRemaining -ErrorAction Ignore
+ $deferHistoryDeadline = $deferHistory | & $Script:CommandTable.'Select-Object' -ExpandProperty DeferDeadline -ErrorAction Ignore
+
+ # Reset switches.
+ $checkDeferDays = $DeferDays -ne 0
+ $checkDeferDeadline = !!$DeferDeadline
+
+ if ($DeferTimes -ne 0)
+ {
+ [System.Int32]$DeferTimes = if ($deferHistoryTimes -ge 0)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Defer history shows [$($deferHistory.DeferTimesRemaining)] deferrals remaining."
+ $deferHistory.DeferTimesRemaining - 1
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The user has [$DeferTimes] deferrals remaining."
+ $DeferTimes - 1
+ }
+
+ if ($DeferTimes -lt 0)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Deferral has expired.'
+ $AllowDefer = $false
+ }
+ }
+
+ if ($checkDeferDays -and $AllowDefer)
+ {
+ $deferDeadlineUniversal = if ($deferHistoryDeadline)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Defer history shows a deadline date of [$deferHistoryDeadline]."
+ & $Script:CommandTable.'Get-ADTUniversalDate' -DateTime $deferHistoryDeadline
+ }
+ else
+ {
+ & $Script:CommandTable.'Get-ADTUniversalDate' -DateTime ([System.DateTime]::Now.AddDays($DeferDays).ToString([System.Globalization.DateTimeFormatInfo]::CurrentInfo.UniversalSortableDateTimePattern))
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The user has until [$deferDeadlineUniversal] before deferral expires."
+
+ if ((& $Script:CommandTable.'Get-ADTUniversalDate') -gt $deferDeadlineUniversal)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Deferral has expired.'
+ $AllowDefer = $false
+ }
+ }
+
+ if ($checkDeferDeadline -and $AllowDefer)
+ {
+ # Validate date.
+ try
+ {
+ $deferDeadlineUniversal = & $Script:CommandTable.'Get-ADTUniversalDate' -DateTime $DeferDeadline
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The user has until [$deferDeadlineUniversal] remaining."
+
+ if ((& $Script:CommandTable.'Get-ADTUniversalDate') -gt $deferDeadlineUniversal)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Deferral has expired.'
+ $AllowDefer = $false
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Date is not in the correct format for the current culture. Type the date in the current locale format, such as 20/08/2014 (Europe) or 08/20/2014 (United States). If the script is intended for multiple cultures, specify the date in the universal sortable date/time format, e.g. '2013-08-22 11:51:52Z'."
+ }
+ }
+ }
+
+ if (($DeferTimes -lt 0) -and !$deferDeadlineUniversal)
+ {
+ $AllowDefer = $false
+ }
+
+ # Prompt the user to close running applications and optionally defer if enabled.
+ if (!$Silent -and (!$adtSession -or !$adtSession.IsSilent()))
+ {
+ # Keep the same variable for countdown to simplify the code.
+ if ($ForceCloseProcessesCountdown -gt 0)
+ {
+ $CloseProcessesCountdown = $ForceCloseProcessesCountdown
+ }
+ elseif ($ForceCountdown -gt 0)
+ {
+ $CloseProcessesCountdown = $ForceCountdown
+ }
+
+ while (($welcomeState.RunningProcesses = & $Script:CommandTable.'Get-ADTRunningProcesses' -ProcessObjects $CloseProcesses) -or (($promptResult -ne 'Defer') -and ($promptResult -ne 'Close')))
+ {
+ # Get all unique running process descriptions.
+ $welcomeState.RunningProcessDescriptions = $welcomeState.RunningProcesses | & $Script:CommandTable.'Select-Object' -ExpandProperty ProcessDescription | & $Script:CommandTable.'Sort-Object' -Unique
+
+ # Define parameters for welcome prompt.
+ $promptParams = @{
+ Title = $PSBoundParameters.Title
+ Subtitle = $PSBoundParameters.Subtitle
+ DeploymentType = $PSBoundParameters.DeploymentType
+ CloseProcessesCountdown = [System.TimeSpan]::FromSeconds($CloseProcessesCountdown)
+ ForceCloseProcessesCountdown = !!$ForceCloseProcessesCountdown
+ ForceCountdown = !!$ForceCountdown
+ PersistPrompt = $PersistPrompt
+ NoMinimizeWindows = $NoMinimizeWindows
+ CustomText = $CustomText
+ NotTopMost = $NotTopMost
+ }
+ if ($CloseProcesses) { $promptParams.Add('ProcessObjects', $CloseProcesses) }
+
+ # Check if we need to prompt the user to defer, to defer and close apps, or not to prompt them at all
+ if ($AllowDefer)
+ {
+ # If there is deferral and closing apps is allowed but there are no apps to be closed, break the while loop.
+ if ($AllowDeferCloseProcesses -and !$welcomeState.RunningProcessDescriptions)
+ {
+ break
+ }
+ elseif (($promptResult -ne 'Close') -or ($welcomeState.RunningProcessDescriptions -and ($promptResult -ne 'Continue')))
+ {
+ # Otherwise, as long as the user has not selected to close the apps or the processes are still running and the user has not selected to continue, prompt user to close running processes with deferral.
+ $deferParams = @{ AllowDefer = $true; DeferTimes = $DeferTimes }; if ($deferDeadlineUniversal) { $deferParams.Add('DeferDeadline', $deferDeadlineUniversal) }
+ $promptResult = & $Script:CommandTable."Show-ADTWelcomePrompt$($adtConfig.UI.DialogStyle)" @promptParams @deferParams
+ }
+ }
+ elseif ($welcomeState.RunningProcessDescriptions -or !!$forceCountdown)
+ {
+ # If there is no deferral and processes are running, prompt the user to close running processes with no deferral option.
+ $promptResult = & $Script:CommandTable."Show-ADTWelcomePrompt$($adtConfig.UI.DialogStyle)" @promptParams
+ }
+ else
+ {
+ # If there is no deferral and no processes running, break the while loop.
+ break
+ }
+
+ # Process the form results.
+ if ($promptResult -eq 'Continue')
+ {
+ # If the user has clicked OK, wait a few seconds for the process to terminate before evaluating the running processes again.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'The user selected to continue...'
+ if (!$welcomeState.RunningProcesses)
+ {
+ # Break the while loop if there are no processes to close and the user has clicked OK to continue.
+ break
+ }
+ [System.Threading.Thread]::Sleep(2000)
+ }
+ elseif ($promptResult -eq 'Close')
+ {
+ # Force the applications to close.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'The user selected to force the application(s) to close...'
+ if ($PromptToSave -and $adtEnv.SessionZero -and !$adtEnv.IsProcessUserInteractive)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Specified [-PromptToSave] option will not be available, because current process is running in session zero and is not interactive.' -Severity 2
+ }
+
+ # Update the process list right before closing, in case it changed.
+ $PromptToSaveTimeout = [System.TimeSpan]::FromSeconds($adtConfig.UI.PromptToSaveTimeout)
+ foreach ($runningProcess in ($welcomeState.RunningProcesses = & $Script:CommandTable.'Get-ADTRunningProcesses' -ProcessObject $CloseProcesses -InformationAction SilentlyContinue))
+ {
+ # If the PromptToSave parameter was specified and the process has a window open, then prompt the user to save work if there is work to be saved when closing window.
+ if ($PromptToSave -and !($adtEnv.SessionZero -and !$adtEnv.IsProcessUserInteractive) -and ($AllOpenWindowsForRunningProcess = & $Script:CommandTable.'Get-ADTWindowTitle' -ParentProcess $runningProcess.ProcessName -InformationAction SilentlyContinue | & $Script:CommandTable.'Select-Object' -First 1) -and ($runningProcess.MainWindowHandle -ne [IntPtr]::Zero))
+ {
+ foreach ($OpenWindow in $AllOpenWindowsForRunningProcess)
+ {
+ try
+ {
+ # Try to bring the window to the front before closing. This doesn't always work.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Stopping process [$($runningProcess.ProcessName)] with window title [$($OpenWindow.WindowTitle)] and prompt to save if there is work to be saved (timeout in [$($adtConfig.UI.PromptToSaveTimeout)] seconds)..."
+ $null = try
+ {
+ [PSADT.GUI.UiAutomation]::BringWindowToFront($OpenWindow.WindowHandle)
+ }
+ catch
+ {
+ $null
+ }
+
+ # Close out the main window and spin until completion.
+ if ($runningProcess.CloseMainWindow())
+ {
+ $promptToSaveStart = [System.DateTime]::Now
+ do
+ {
+ if (!($IsWindowOpen = & $Script:CommandTable.'Get-ADTWindowTitle' -WindowHandle $OpenWindow.WindowHandle -InformationAction SilentlyContinue | & $Script:CommandTable.'Select-Object' -First 1))
+ {
+ break
+ }
+ [System.Threading.Thread]::Sleep(3000)
+ }
+ while (($IsWindowOpen) -and (([System.DateTime]::Now - $promptToSaveStart) -lt $PromptToSaveTimeout))
+
+ if ($IsWindowOpen)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Exceeded the [$($adtConfig.UI.PromptToSaveTimeout)] seconds timeout value for the user to save work associated with process [$($runningProcess.ProcessName)] with window title [$($OpenWindow.WindowTitle)]." -Severity 2
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Window [$($OpenWindow.WindowTitle)] for process [$($runningProcess.ProcessName)] was successfully closed."
+ }
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to call the CloseMainWindow() method on process [$($runningProcess.ProcessName)] with window title [$($OpenWindow.WindowTitle)] because the main window may be disabled due to a modal dialog being shown." -Severity 3
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to close window [$($OpenWindow.WindowTitle)] for process [$($runningProcess.ProcessName)].`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 3
+ }
+ finally
+ {
+ $runningProcess.Refresh()
+ }
+ }
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Stopping process $($runningProcess.ProcessName)..."
+ & $Script:CommandTable.'Stop-Process' -Name $runningProcess.ProcessName -Force -ErrorAction Ignore
+ }
+ }
+
+ if ($welcomeState.RunningProcesses = & $Script:CommandTable.'Get-ADTRunningProcesses' -ProcessObjects $CloseProcesses -InformationAction SilentlyContinue)
+ {
+ # Apps are still running, give them 2s to close. If they are still running, the Welcome Window will be displayed again.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Sleeping for 2 seconds because the processes are still not closed...'
+ [System.Threading.Thread]::Sleep(2000)
+ }
+ }
+ elseif ($promptResult -eq 'Timeout')
+ {
+ # Stop the script (if not actioned before the timeout value).
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Deployment not actioned before the timeout value.'
+ $BlockExecution = $false
+ if ($adtSession -and (($DeferTimes -ge 0) -or $deferDeadlineUniversal))
+ {
+ & $Script:CommandTable.'Set-ADTDeferHistory' -DeferTimesRemaining $DeferTimes -DeferDeadline $deferDeadlineUniversal
+ }
+
+ # Dispose the welcome prompt timer here because if we dispose it within the Show-ADTWelcomePrompt function we risk resetting the timer and missing the specified timeout period.
+ if ($welcomeState.WelcomeTimer)
+ {
+ $welcomeState.WelcomeTimer.Dispose()
+ $welcomeState.WelcomeTimer = $null
+ }
+
+ # Restore minimized windows.
+ if (!$NoMinimizeWindows)
+ {
+ $null = $adtEnv.ShellApp.UndoMinimizeAll()
+ }
+ if ($adtSession)
+ {
+ & $Script:CommandTable.'Close-ADTSession' -ExitCode $adtConfig.UI.DefaultExitCode
+ }
+ }
+ elseif ($promptResult -eq 'Defer')
+ {
+ # Stop the script (user chose to defer)
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Deployment deferred by the user.'
+ $BlockExecution = $false
+ & $Script:CommandTable.'Set-ADTDeferHistory' -DeferTimesRemaining $DeferTimes -DeferDeadline $deferDeadlineUniversal
+
+ # Restore minimized windows.
+ if (!$NoMinimizeWindows)
+ {
+ $null = $adtEnv.ShellApp.UndoMinimizeAll()
+ }
+ if ($adtSession)
+ {
+ & $Script:CommandTable.'Close-ADTSession' -ExitCode $adtConfig.UI.DeferExitCode
+ }
+ }
+ }
+ }
+
+ # Force the processes to close silently, without prompting the user.
+ if ($Silent -and ($runningProcesses = & $Script:CommandTable.'Get-ADTRunningProcesses' -ProcessObjects $CloseProcesses -InformationAction SilentlyContinue))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Force closing application(s) [$(($runningProcesses.ProcessDescription | & $Script:CommandTable.'Sort-Object' -Unique) -join ',')] without prompting user."
+ & $Script:CommandTable.'Stop-Process' -InputObject $runningProcesses -Force -ErrorAction Ignore
+ [System.Threading.Thread]::Sleep(2000)
+ }
+
+ # If block execution switch is true, call the function to block execution of these processes.
+ if ($BlockExecution -and $CloseProcesses)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message '[-BlockExecution] parameter specified.'
+ & $Script:CommandTable.'Block-ADTAppExecution' -ProcessName $CloseProcesses.Name
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Start-ADTMsiProcess
+#
+#-----------------------------------------------------------------------------
+
+function Start-ADTMsiProcess
+{
+ <#
+ .SYNOPSIS
+ Executes msiexec.exe to perform actions such as install, uninstall, patch, repair, or active setup for MSI and MSP files or MSI product codes.
+
+ .DESCRIPTION
+ This function utilizes msiexec.exe to handle various operations on MSI and MSP files, as well as MSI product codes. The operations include installation, uninstallation, patching, repair, and setting up active configurations.
+
+ If the -Action parameter is set to "Install" and the MSI is already installed, the function will terminate without performing any actions.
+
+ The function automatically sets default switches for msiexec based on preferences defined in the config.psd1 file. Additionally, it generates a log file name and creates a verbose log for all msiexec operations, ensuring detailed tracking.
+
+ The MSI or MSP file is expected to reside in the "Files" subdirectory of the App Deploy Toolkit, with transform files expected to be in the same directory as the MSI file.
+
+ .PARAMETER Action
+ Specifies the action to be performed. Available options: Install, Uninstall, Patch, Repair, ActiveSetup.
+
+ .PARAMETER FilePath
+ The file path to the MSI/MSP file.
+
+ .PARAMETER ProductCode
+ The product code of the installed MSI.
+
+ .PARAMETER InstalledApplication
+ The InstalledApplication object of the installed MSI.
+
+ .PARAMETER Transforms
+ The name(s) of the transform file(s) to be applied to the MSI. The transform files should be in the same directory as the MSI file.
+
+ .PARAMETER Patches
+ The name(s) of the patch (MSP) file(s) to be applied to the MSI for the "Install" action. The patch files should be in the same directory as the MSI file.
+
+ .PARAMETER ArgumentList
+ Overrides the default parameters specified in the config.psd1 file.
+
+ .PARAMETER AdditionalArgumentList
+ Adds additional parameters to the default set specified in the config.psd1 file.
+
+ .PARAMETER SecureArgumentList
+ Hides all parameters passed to the MSI or MSP file from the toolkit log file.
+
+ .PARAMETER LoggingOptions
+ Overrides the default logging options specified in the config.psd1 file.
+
+ .PARAMETER LogFileName
+ Overrides the default log file name. The default log file name is generated from the MSI file name. If LogFileName does not end in .log, it will be automatically appended.
+
+ For uninstallations, by default the product code is resolved to the DisplayName and version of the application.
+
+ .PARAMETER WorkingDirectory
+ Overrides the working directory. The working directory is set to the location of the MSI file.
+
+ .PARAMETER SkipMSIAlreadyInstalledCheck
+ Skips the check to determine if the MSI is already installed on the system.
+
+ .PARAMETER IncludeUpdatesAndHotfixes
+ Include matches against updates and hotfixes in results.
+
+ .PARAMETER NoWait
+ Immediately continue after executing the process.
+
+ .PARAMETER PassThru
+ Returns ExitCode, STDOut, and STDErr output from the process.
+
+ .PARAMETER SuccessExitCodes
+ List of exit codes to be considered successful. Defaults to values set during ADTSession initialization, otherwise: 0
+
+ .PARAMETER RebootExitCodes
+ List of exit codes to indicate a reboot is required. Defaults to values set during ADTSession initialization, otherwise: 1641, 3010
+
+ .PARAMETER IgnoreExitCodes
+ List the exit codes to ignore or * to ignore all exit codes.
+
+ .PARAMETER PriorityClass
+ Specifies priority class for the process. Options: Idle, Normal, High, AboveNormal, BelowNormal, RealTime. Default: Normal
+
+ .PARAMETER RepairFromSource
+ Specifies whether we should repair from source. Also rewrites local cache. Default: $false
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.ProcessResult
+
+ Returns an object with the results of the installation if -PassThru is specified.
+ - ExitCode
+ - StdOut
+ - StdErr
+
+ .EXAMPLE
+ Start-ADTMsiProcess -Action 'Install' -FilePath 'Adobe_FlashPlayer_11.2.202.233_x64_EN.msi'
+
+ Install an MSI.
+
+ .EXAMPLE
+ Start-ADTMsiProcess -Action 'Install' -FilePath 'Adobe_FlashPlayer_11.2.202.233_x64_EN.msi' -Transforms 'Adobe_FlashPlayer_11.2.202.233_x64_EN_01.mst' -ArgumentList '/QN'
+
+ Install an MSI, applying a transform and overriding the default MSI toolkit parameters.
+
+ .EXAMPLE
+ $ExecuteMSIResult = Start-ADTMsiProcess -Action 'Install' -FilePath 'Adobe_FlashPlayer_11.2.202.233_x64_EN.msi' -PassThru
+
+ Install an MSI and stores the result of the execution into a variable by using the -PassThru option.
+
+ .EXAMPLE
+ Start-ADTMsiProcess -Action 'Uninstall' -ProductCode '{26923b43-4d38-484f-9b9e-de460746276c}'
+
+ Uninstall an MSI using a product code.
+
+ .EXAMPLE
+ Start-ADTMsiProcess -Action 'Patch' -FilePath 'Adobe_Reader_11.0.3_EN.msp'
+
+ Install an MSP.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Start-ADTMsiProcess
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Int32])]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('Install', 'Uninstall', 'Patch', 'Repair', 'ActiveSetup')]
+ [System.String]$Action = 'Install',
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'FilePath', ValueFromPipeline = $true, HelpMessage = 'Please supply the path to the MSI/MSP file to process.')]
+ [ValidateScript({
+ if ([System.IO.Path]::GetExtension($_) -notmatch '^\.ms[ip]$')
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified input has an invalid file extension.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'ProductCode', ValueFromPipeline = $true, HelpMessage = 'Please supply the Product Code to process.')]
+ [ValidateNotNullOrEmpty()]
+ [System.Guid]$ProductCode,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'InstalledApplication', ValueFromPipeline = $true, HelpMessage = 'Please supply the InstalledApplication object to process.')]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Types.InstalledApplication]$InstalledApplication,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Transforms,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = 'Install (Normal): (Get-ADTConfig).MSI.InstallParams; Install (Silent): (Get-ADTConfig).MSI.SilentParams; Uninstall (Normal): (Get-ADTConfig).MSI.UninstallParams; Uninstall (Silent): (Get-ADTConfig).MSI.SilentParams')]
+ [System.String[]]$ArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$AdditionalArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SecureArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Patches,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LoggingOptions,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateScript({
+ if ([System.String]::IsNullOrWhiteSpace($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName LogFileName -ProvidedValue $_ -ExceptionMessage 'The specified input is null or white space.'))
+ }
+ if ([System.IO.Path]::GetExtension($_) -match '^\.(log|txt)$')
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName LogFileName -ProvidedValue $_ -ExceptionMessage 'The specified input cannot have an extension.'))
+ }
+ return $true
+ })]
+ [System.String]$LogFileName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipMSIAlreadyInstalledCheck,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$IncludeUpdatesAndHotfixes,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32[]]$SuccessExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32[]]$RebootExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$IgnoreExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Diagnostics.ProcessPriorityClass]$PriorityClass = [System.Diagnostics.ProcessPriorityClass]::Normal,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$RepairFromSource
+ )
+
+ begin
+ {
+ # The use of a ProductCode with an Install action is not supported.
+ if ($ProductCode -and ($Action -eq 'Install'))
+ {
+ $naerParams = @{
+ Exception = [System.InvalidOperationException]::new("The ProductCode parameter can only be used with non-install actions.")
+ Category = [System.Management.Automation.ErrorCategory]::InvalidOperation
+ ErrorId = 'ProductCodeInstallActionNotSupported'
+ TargetObject = $PSBoundParameters
+ RecommendedAction = "Please review the supplied parameters and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet; $adtConfig = & $Script:CommandTable.'Get-ADTConfig'
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Determine whether the input is a ProductCode or not.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Executing MSI action [$Action]..."
+
+ # If the MSI is in the Files directory, set the full path to the MSI.
+ $msiProduct = switch ($PSCmdlet.ParameterSetName)
+ {
+ FilePath
+ {
+ if (& $Script:CommandTable.'Test-Path' -LiteralPath $FilePath -PathType Leaf)
+ {
+ (& $Script:CommandTable.'Get-Item' -LiteralPath $FilePath).FullName
+ }
+ elseif ($adtSession -and [System.IO.File]::Exists(($dirFilesPath = [System.IO.Path]::Combine($adtSession.DirFiles, $FilePath))))
+ {
+ $dirFilesPath
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to find the file [$FilePath]." -Severity 3
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("Failed to find the file [$FilePath].")
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'FilePathNotFound'
+ TargetObject = $FilePath
+ RecommendedAction = "Please confirm the path of the file and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ break
+ }
+
+ ProductCode
+ {
+ $ProductCode.ToString('B')
+ break
+ }
+
+ InstalledApplication
+ {
+ $InstalledApplication.ProductCode.ToString('B')
+ break
+ }
+ }
+
+ # Fix up any bad file paths.
+ if ([System.IO.Path]::GetExtension($msiProduct) -eq '.msi')
+ {
+ # Iterate transforms.
+ if ($Transforms)
+ {
+ for ($i = 0; $i -lt $Transforms.Length; $i++)
+ {
+ if ([System.IO.File]::Exists(($fullPath = & $Script:CommandTable.'Join-Path' -Path (& $Script:CommandTable.'Split-Path' -Path $msiProduct -Parent) -ChildPath $Transforms[$i].Replace('.\', ''))))
+ {
+ $Transforms[$i] = $fullPath
+ }
+ }
+ }
+
+ # Iterate patches.
+ if ($Patches)
+ {
+ for ($i = 0; $i -lt $Patches.Length; $i++)
+ {
+ if ([System.IO.File]::Exists(($fullPath = & $Script:CommandTable.'Join-Path' -Path (& $Script:CommandTable.'Split-Path' -Path $msiProduct -Parent) -ChildPath $Patches[$i].Replace('.\', ''))))
+ {
+ $Patches[$i] = $fullPath
+ }
+ }
+ }
+ }
+
+ # If the provided MSI was a file path, get the Property table and store it.
+ $msiPropertyTable = if ([System.IO.Path]::GetExtension($msiProduct) -eq '.msi')
+ {
+ $gmtpParams = @{ Path = $msiProduct; Table = 'Property' }; if ($Transforms) { $gmtpParams.Add('TransformPath', $Transforms) }
+ & $Script:CommandTable.'Get-ADTMsiTableProperty' @gmtpParams
+ }
+
+ # Get the ProductCode of the MSI.
+ $msiProductCode = if ($ProductCode)
+ {
+ $ProductCode
+ }
+ elseif ($InstalledApplication)
+ {
+ $InstalledApplication.ProductCode
+ }
+ elseif ($msiPropertyTable)
+ {
+ $msiPropertyTable.ProductCode
+ }
+
+ # Check if the MSI is already installed. If no valid ProductCode to check or SkipMSIAlreadyInstalledCheck supplied, then continue with requested MSI action.
+ $msiInstalled = if ($msiProductCode -and !$SkipMSIAlreadyInstalledCheck)
+ {
+ if (!$InstalledApplication -and ($installedApps = & $Script:CommandTable.'Get-ADTApplication' -ProductCode $msiProductCode -IncludeUpdatesAndHotfixes:$IncludeUpdatesAndHotfixes))
+ {
+ $InstalledApplication = $installedApps
+ }
+ !!$InstalledApplication
+ }
+ else
+ {
+ $Action -ne 'Install'
+ }
+
+ # Return early if we're installing an installed product, or anything else for a non-installed product.
+ if ($msiInstalled -and ($Action -eq 'Install'))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The MSI is already installed on this system. Skipping action [$Action]..."
+ return $(if ($PassThru) { [PSADT.Types.ProcessResult]::new(1638, $null, $null) })
+ }
+ elseif (!$msiInstalled -and ($Action -ne 'Install'))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The MSI is not installed on this system. Skipping action [$Action]..."
+ return
+ }
+
+ # Set up the log file to use.
+ $logFile = if ($PSBoundParameters.ContainsKey('LogFileName'))
+ {
+ $LogFileName.Trim()
+ }
+ elseif ($InstalledApplication)
+ {
+ (& $Script:CommandTable.'Remove-ADTInvalidFileNameChars' -Name ($InstalledApplication.DisplayName + '_' + $InstalledApplication.DisplayVersion)) -replace '\s+'
+ }
+ elseif ($msiPropertyTable)
+ {
+ (& $Script:CommandTable.'Remove-ADTInvalidFileNameChars' -Name ($msiPropertyTable.ProductName + '_' + $msiPropertyTable.ProductVersion)) -replace '\s+'
+ }
+
+ # Build the log path to use.
+ $logPath = if ($logFile)
+ {
+ if ($adtSession -and $adtConfig.Toolkit.CompressLogs)
+ {
+ & $Script:CommandTable.'Join-Path' -Path $adtSession.LogTempFolder -ChildPath $logFile
+ }
+ else
+ {
+ # Create the Log directory if it doesn't already exist.
+ if (![System.IO.Directory]::Exists($adtConfig.MSI.LogPath))
+ {
+ $null = [System.IO.Directory]::CreateDirectory($adtConfig.MSI.LogPath)
+ }
+
+ # Build the log file path.
+ & $Script:CommandTable.'Join-Path' -Path $adtConfig.MSI.LogPath -ChildPath $logFile
+ }
+ }
+
+ # Set the installation parameters.
+ if ($adtSession -and $adtSession.IsNonInteractive())
+ {
+ $msiInstallDefaultParams = $adtConfig.MSI.SilentParams
+ $msiUninstallDefaultParams = $adtConfig.MSI.SilentParams
+ }
+ else
+ {
+ $msiInstallDefaultParams = $adtConfig.MSI.InstallParams
+ $msiUninstallDefaultParams = $adtConfig.MSI.UninstallParams
+ }
+
+ # Build the MSI parameters.
+ switch ($action)
+ {
+ Install
+ {
+ $option = '/i'
+ $msiLogFile = if ($logPath) { "$($logPath)_$($_)" }
+ $msiDefaultParams = $msiInstallDefaultParams
+ break
+ }
+ Uninstall
+ {
+ $option = '/x'
+ $msiLogFile = if ($logPath) { "$($logPath)_$($_)" }
+ $msiDefaultParams = $msiUninstallDefaultParams
+ break
+ }
+ Patch
+ {
+ $option = '/update'
+ $msiLogFile = if ($logPath) { "$($logPath)_$($_)" }
+ $msiDefaultParams = $msiInstallDefaultParams
+ break
+ }
+ Repair
+ {
+ $option = "/f$(if ($RepairFromSource) {'vomus'})"
+ $msiLogFile = if ($logPath) { "$($logPath)_$($_)" }
+ $msiDefaultParams = $msiInstallDefaultParams
+ break
+ }
+ ActiveSetup
+ {
+ $option = '/fups'
+ $msiLogFile = if ($logPath) { "$($logPath)_$($_)" }
+ $msiDefaultParams = $null
+ break
+ }
+ }
+
+ # Post-process the MSI log file variable.
+ if ($msiLogFile)
+ {
+ # Append the username to the log file name if the toolkit is not running as an administrator, since users do not have the rights to modify files in the ProgramData folder that belong to other users.
+ if (!(& $Script:CommandTable.'Test-ADTCallerIsAdmin'))
+ {
+ $msiLogFile = $msiLogFile + '_' + (& $Script:CommandTable.'Remove-ADTInvalidFileNameChars' -Name ([System.Environment]::UserName))
+ }
+
+ # Append ".log" to the MSI logfile path and enclose in quotes.
+ if ([IO.Path]::GetExtension($msiLogFile) -ne '.log')
+ {
+ $msiLogFile = "`"$($msiLogFile + '.log')`""
+ }
+ }
+
+ # Set the working directory of the MSI.
+ if ($PSCmdlet.ParameterSetName.Equals('FilePath') -and !$workingDirectory)
+ {
+ $WorkingDirectory = [System.IO.Path]::GetDirectoryName($msiProduct)
+ }
+
+ # Enumerate all transforms specified, qualify the full path if possible and enclose in quotes.
+ $mstFile = if ($Transforms)
+ {
+ "`"$($Transforms -join ';')`""
+ }
+
+ # Enumerate all patches specified, qualify the full path if possible and enclose in quotes.
+ $mspFile = if ($Patches)
+ {
+ "`"$($Patches -join ';')`""
+ }
+
+ # Start building the MsiExec command line starting with the base action and file.
+ $argsMSI = "$option `"$msiProduct`""
+
+ # Add MST.
+ if ($mstFile)
+ {
+ $argsMSI = "$argsMSI TRANSFORMS=$mstFile TRANSFORMSSECURE=1"
+ }
+
+ # Add MSP.
+ if ($mspFile)
+ {
+ $argsMSI = "$argsMSI PATCH=$mspFile"
+ }
+
+ # Replace default parameters if specified.
+ $argsMSI = if ($ArgumentList)
+ {
+ "$argsMSI $([System.String]::Join(' ', $ArgumentList))"
+ }
+ else
+ {
+ "$argsMSI $msiDefaultParams"
+ }
+
+ # Add reinstallmode and reinstall variable for Patch.
+ if ($action -eq 'Patch')
+ {
+ $argsMSI = "$argsMSI REINSTALLMODE=ecmus REINSTALL=ALL"
+ }
+
+ # Append parameters to default parameters if specified.
+ if ($AdditionalArgumentList)
+ {
+ $argsMSI = "$argsMSI $([System.String]::Join(' ', $AdditionalArgumentList))"
+ }
+
+ # Add custom Logging Options if specified, otherwise, add default Logging Options from Config file.
+ if ($msiLogFile)
+ {
+ $argsMSI = if ($LoggingOptions)
+ {
+ "$argsMSI $LoggingOptions $msiLogFile"
+ }
+ else
+ {
+ "$argsMSI $($adtConfig.MSI.LoggingOptions) $msiLogFile"
+ }
+ }
+
+ # Build the hashtable with the options that will be passed to Start-ADTProcess using splatting.
+ $ExecuteProcessSplat = @{
+ FilePath = "$([System.Environment]::SystemDirectory)\msiexec.exe"
+ ArgumentList = $argsMSI
+ WindowStyle = 'Normal'
+ }
+ if ($WorkingDirectory)
+ {
+ $ExecuteProcessSplat.Add('WorkingDirectory', $WorkingDirectory)
+ }
+ if ($SecureArgumentList)
+ {
+ $ExecuteProcessSplat.Add('SecureArgumentList', $SecureArgumentList)
+ }
+ if ($PassThru)
+ {
+ $ExecuteProcessSplat.Add('PassThru', $PassThru)
+ }
+ if ($SuccessExitCodes)
+ {
+ $ExecuteProcessSplat.Add('SuccessExitCodes', $SuccessExitCodes)
+ }
+ if ($RebootExitCodes)
+ {
+ $ExecuteProcessSplat.Add('RebootExitCodes', $RebootExitCodes)
+ }
+ if ($IgnoreExitCodes)
+ {
+ $ExecuteProcessSplat.Add('IgnoreExitCodes', $IgnoreExitCodes)
+ }
+ if ($PriorityClass)
+ {
+ $ExecuteProcessSplat.Add('PriorityClass', $PriorityClass)
+ }
+ if ($NoWait)
+ {
+ $ExecuteProcessSplat.Add('NoWait', $NoWait)
+ }
+
+ # Call the Start-ADTProcess function.
+ $result = & $Script:CommandTable.'Start-ADTProcess' @ExecuteProcessSplat
+
+ # Refresh environment variables for Windows Explorer process as Windows does not consistently update environment variables created by MSIs.
+ & $Script:CommandTable.'Update-ADTDesktop'
+
+ # Return the results if passing through.
+ if ($PassThru -and $result)
+ {
+ return $result
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Start-ADTMspProcess
+#
+#-----------------------------------------------------------------------------
+
+function Start-ADTMspProcess
+{
+ <#
+ .SYNOPSIS
+ Executes an MSP file using the same logic as Start-ADTMsiProcess.
+
+ .DESCRIPTION
+ Reads SummaryInfo targeted product codes in MSP file and determines if the MSP file applies to any installed products. If a valid installed product is found, triggers the Start-ADTMsiProcess function to patch the installation.
+
+ Uses default config MSI parameters. You can use -AdditionalArgumentList to add additional parameters.
+
+ .PARAMETER FilePath
+ Path to the MSP file.
+
+ .PARAMETER AdditionalArgumentList
+ Additional parameters.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Start-ADTMspProcess -FilePath 'Adobe_Reader_11.0.3_EN.msp'
+
+ Executes the specified MSP file for Adobe Reader 11.0.3.
+
+ .EXAMPLE
+ Start-ADTMspProcess -FilePath 'AcroRdr2017Upd1701130143_MUI.msp' -AdditionalArgumentList 'ALLUSERS=1'
+
+ Executes the specified MSP file for Acrobat Reader 2017 with additional parameters.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Start-ADTMspProcess
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Int32])]
+ param
+ (
+ [Parameter(Mandatory = $true, HelpMessage = 'Please supply the path to the MSP file to process.')]
+ [ValidateScript({
+ if ([System.IO.Path]::GetExtension($_) -notmatch '^\.msp$')
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified input has an invalid file extension.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$AdditionalArgumentList
+ )
+
+ begin
+ {
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If the MSP is in the Files directory, set the full path to the MSP.
+ $mspFile = if ($adtSession -and [System.IO.File]::Exists(($dirFilesPath = [System.IO.Path]::Combine($adtSession.DirFiles, $FilePath))))
+ {
+ $dirFilesPath
+ }
+ elseif (& $Script:CommandTable.'Test-Path' -LiteralPath $FilePath)
+ {
+ (& $Script:CommandTable.'Get-Item' -LiteralPath $FilePath).FullName
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Failed to find MSP file [$FilePath]." -Severity 3
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("Failed to find MSP file [$FilePath].")
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'MsiFileNotFound'
+ TargetObject = $FilePath
+ RecommendedAction = "Please confirm the path of the MSP file and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Create a Windows Installer object and open the database in read-only mode.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Checking MSP file for valid product codes.'
+ [__ComObject]$Installer = & $Script:CommandTable.'New-Object' -ComObject WindowsInstaller.Installer
+ [__ComObject]$Database = & $Script:CommandTable.'Invoke-ADTObjectMethod' -InputObject $Installer -MethodName OpenDatabase -ArgumentList @($mspFile, 32)
+
+ # Get the SummaryInformation from the Windows Installer database and store all product codes found.
+ [__ComObject]$SummaryInformation = & $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $Database -PropertyName SummaryInformation
+ $AllTargetedProductCodes = & $Script:CommandTable.'Get-ADTApplication' -ProductCode (& $Script:CommandTable.'Get-ADTObjectProperty' -InputObject $SummaryInformation -PropertyName Property -ArgumentList @(7)).Split(';')
+
+ # Free our COM objects.
+ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($SummaryInformation)
+ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Database)
+ [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Installer)
+
+ # If the application is installed, patch it.
+ if ($AllTargetedProductCodes)
+ {
+ & $Script:CommandTable.'Start-ADTMsiProcess' -Action Patch @PSBoundParameters
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Start-ADTProcess
+#
+#-----------------------------------------------------------------------------
+
+function Start-ADTProcess
+{
+ <#
+ .SYNOPSIS
+ Execute a process with optional arguments, working directory, window style.
+
+ .DESCRIPTION
+ Executes a process, e.g. a file included in the Files directory of the App Deploy Toolkit, or a file on the local machine. Provides various options for handling the return codes (see Parameters).
+
+ .PARAMETER FilePath
+ Path to the file to be executed. If the file is located directly in the "Files" directory of the App Deploy Toolkit, only the file name needs to be specified.
+
+ Otherwise, the full path of the file must be specified. If the files is in a subdirectory of "Files", use the "$($adtSession.DirFiles)" variable as shown in the example.
+
+ .PARAMETER ArgumentList
+ Arguments to be passed to the executable.
+
+ .PARAMETER SecureArgumentList
+ Hides all parameters passed to the executable from the Toolkit log file.
+
+ .PARAMETER WindowStyle
+ Style of the window of the process executed. Options: Normal, Hidden, Maximized, Minimized. Default: Normal. Only works for native Windows GUI applications. If the WindowStyle is set to Hidden, UseShellExecute should be set to $true.
+
+ Note: Not all processes honor WindowStyle. WindowStyle is a recommendation passed to the process. They can choose to ignore it.
+
+ .PARAMETER CreateNoWindow
+ Specifies whether the process should be started with a new window to contain it. Only works for Console mode applications. UseShellExecute should be set to $false. Default is false.
+
+ .PARAMETER WorkingDirectory
+ The working directory used for executing the process. Defaults to the directory of the file being executed. The use of UseShellExecute affects this parameter.
+
+ .PARAMETER NoWait
+ Immediately continue after executing the process.
+
+ .PARAMETER PassThru
+ If NoWait is not specified, returns an object with ExitCode, STDOut and STDErr output from the process. If NoWait is specified, returns an object with Id, Handle and ProcessName.
+
+ .PARAMETER WaitForMsiExec
+ Sometimes an EXE bootstrapper will launch an MSI install. In such cases, this variable will ensure that this function waits for the msiexec engine to become available before starting the install.
+
+ .PARAMETER MsiExecWaitTime
+ Specify the length of time in seconds to wait for the msiexec engine to become available. Default: 600 seconds (10 minutes).
+
+ .PARAMETER SuccessExitCodes
+ List of exit codes to be considered successful. Defaults to values set during ADTSession initialization, otherwise: 0
+
+ .PARAMETER RebootExitCodes
+ List of exit codes to indicate a reboot is required. Defaults to values set during ADTSession initialization, otherwise: 1641, 3010
+
+ .PARAMETER IgnoreExitCodes
+ List the exit codes to ignore or * to ignore all exit codes.
+
+ .PARAMETER PriorityClass
+ Specifies priority class for the process. Options: Idle, Normal, High, AboveNormal, BelowNormal, RealTime. Default: Normal
+
+ .PARAMETER UseShellExecute
+ Specifies whether to use the operating system shell to start the process. $true if the shell should be used when starting the process; $false if the process should be created directly from the executable file.
+
+ The word "Shell" in this context refers to a graphical shell (similar to the Windows shell) rather than command shells (for example, bash or sh) and lets users launch graphical applications or open documents. It lets you open a file or a url and the Shell will figure out the program to open it with.
+
+ The WorkingDirectory property behaves differently depending on the value of the UseShellExecute property. When UseShellExecute is true, the WorkingDirectory property specifies the location of the executable. When UseShellExecute is false, the WorkingDirectory property is not used to find the executable. Instead, it is used only by the process that is started and has meaning only within the context of the new process.
+
+ If you set UseShellExecute to $true, there will be no available output from the process.
+
+ .EXAMPLE
+ Start-ADTProcess -FilePath 'setup.exe' -ArgumentList '/S' -IgnoreExitCodes 1,2
+
+ Launch InstallShield "setup.exe" from the ".\Files" sub-directory.
+
+ .EXAMPLE
+ Start-ADTProcess -FilePath "$($adtSession.DirFiles)\Bin\setup.exe" -ArgumentList '/S' -WindowStyle 'Hidden'
+
+ Launch InstallShield "setup.exe" from the ".\Files\Bin" sub-directory.
+
+ .EXAMPLE
+ Start-ADTProcess -FilePath 'uninstall_flash_player_64bit.exe' -ArgumentList '/uninstall' -WindowStyle 'Hidden'
+
+ If the file is in the "Files" directory of the AppDeployToolkit, only the file name needs to be specified.
+
+ .EXAMPLE
+ Start-ADTProcess -FilePath 'setup.exe' -ArgumentList "-s -f2`"$((Get-ADTConfig).Toolkit.LogPath)\$($adtSession.InstallName).log`""
+
+ Launch InstallShield "setup.exe" from the ".\Files" sub-directory and force log files to the logging folder.
+
+ .EXAMPLE
+ Start-ADTProcess -FilePath 'setup.exe' -ArgumentList "/s /v`"ALLUSERS=1 /qn /L* `"$((Get-ADTConfig).Toolkit.LogPath)\$($adtSession.InstallName).log`"`""
+
+ Launch InstallShield "setup.exe" with embedded MSI and force log files to the logging folder.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.ProcessResult
+
+ Returns an object with the results of the installation if -PassThru is specified.
+ - ExitCode
+ - StdOut
+ - StdErr
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Start-ADTProcess
+ #>
+
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.ProcessResult])]
+ [OutputType([PSADT.Types.ProcessInfo])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SecureArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Diagnostics.ProcessWindowStyle]$WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Normal,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$CreateNoWindow,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$NoWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$WaitForMsiExec,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$MsiExecWaitTime,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32[]]$SuccessExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32[]]$RebootExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [SupportsWildcards()]
+ [System.String[]]$IgnoreExitCodes,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Diagnostics.ProcessPriorityClass]$PriorityClass = [System.Diagnostics.ProcessPriorityClass]::Normal,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$UseShellExecute
+ )
+
+ begin
+ {
+ # Initalize function and get required objects.
+ $adtSession = & $Script:CommandTable.'Initialize-ADTModuleIfUnitialized' -Cmdlet $PSCmdlet
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Set up defaults if not specified.
+ if (!$PSBoundParameters.ContainsKey('MsiExecWaitTime'))
+ {
+ $MsiExecWaitTime = (& $Script:CommandTable.'Get-ADTConfig').MSI.MutexWaitTime
+ }
+ if (!$PSBoundParameters.ContainsKey('SuccessExitCodes'))
+ {
+ $SuccessExitCodes = if ($adtSession)
+ {
+ $adtSession.AppSuccessExitCodes
+ }
+ else
+ {
+ 0
+ }
+ }
+ if (!$PSBoundParameters.ContainsKey('RebootExitCodes'))
+ {
+ $RebootExitCodes = if ($adtSession)
+ {
+ $adtSession.AppRebootExitCodes
+ }
+ else
+ {
+ 1641, 3010
+ }
+ }
+
+ # Set up initial variables.
+ $extInvoker = !(& $Script:CommandTable.'Get-PSCallStack')[1].InvocationInfo.MyCommand.Source.StartsWith($MyInvocation.MyCommand.Module.Name)
+ $stdOutBuilder = [System.Text.StringBuilder]::new()
+ $stdErrBuilder = [System.Text.StringBuilder]::new()
+ $stdOutEvent = $stdErrEvent = $null
+ $stdOut = $stdErr = $null
+ $returnCode = $null
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Validate and find the fully qualified path for the $FilePath variable.
+ if ([System.IO.Path]::IsPathRooted($FilePath) -and [System.IO.Path]::HasExtension($FilePath))
+ {
+ if (![System.IO.File]::Exists($FilePath))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "File [$FilePath] not found." -Severity 3
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("File [$FilePath] not found.")
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'PathFileNotFound'
+ TargetObject = $FilePath
+ RecommendedAction = "Please confirm the path of the specified file and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "[$FilePath] is a valid fully qualified path, continue."
+ }
+ else
+ {
+ # Get the fully qualified path for the file using DirFiles, the current directory, then the system's path environment variable.
+ if (!($fqPath = & $Script:CommandTable.'Get-Item' -Path ("$(if ($adtSession) { "$($adtSession.DirFiles);" })$($ExecutionContext.SessionState.Path.CurrentLocation.Path);$([System.Environment]::GetEnvironmentVariable('PATH'))".TrimEnd(';').Split(';').TrimEnd('\') -replace '$', "\$FilePath") -ErrorAction Ignore | & $Script:CommandTable.'Select-Object' -ExpandProperty FullName -First 1))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "[$FilePath] contains an invalid path or file name." -Severity 3
+ $naerParams = @{
+ Exception = [System.IO.FileNotFoundException]::new("[$FilePath] contains an invalid path or file name.")
+ Category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
+ ErrorId = 'PathFileNotFound'
+ TargetObject = $FilePath
+ RecommendedAction = "Please confirm the path of the specified file and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "[$FilePath] successfully resolved to fully qualified path [$fqPath]."
+ $FilePath = $fqPath
+ }
+
+ # Set the Working directory if not specified.
+ if (!$WorkingDirectory)
+ {
+ $WorkingDirectory = [System.IO.Path]::GetDirectoryName($FilePath)
+ }
+
+ # If the WindowStyle parameter is set to 'Hidden', set the UseShellExecute parameter to '$true' unless specifically specified.
+ if ($WindowStyle.Equals([System.Diagnostics.ProcessWindowStyle]::Hidden) -and !$PSBoundParameters.ContainsKey('UseShellExecute'))
+ {
+ $UseShellExecute = $true
+ }
+
+ # If MSI install, check to see if the MSI installer service is available or if another MSI install is already underway.
+ # Please note that a race condition is possible after this check where another process waiting for the MSI installer
+ # to become available grabs the MSI Installer mutex before we do. Not too concerned about this possible race condition.
+ if (($FilePath -match 'msiexec') -or $WaitForMsiExec)
+ {
+ $MsiExecAvailable = & $Script:CommandTable.'Test-ADTMutexAvailability' -MutexName 'Global\_MSIExecute' -MutexWaitTime ([System.TimeSpan]::FromSeconds($MsiExecWaitTime))
+ [System.Threading.Thread]::Sleep(1000)
+ if (!$MsiExecAvailable)
+ {
+ # Default MSI exit code for install already in progress.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Another MSI installation is already in progress and needs to be completed before proceeding with this installation.' -Severity 3
+ $returnCode = 1618
+ $naerParams = @{
+ Exception = [System.TimeoutException]::new('Another MSI installation is already in progress and needs to be completed before proceeding with this installation.')
+ Category = [System.Management.Automation.ErrorCategory]::ResourceBusy
+ ErrorId = 'MsiExecUnavailable'
+ TargetObject = $FilePath
+ RecommendedAction = "Please wait for the current MSI operation to finish and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ }
+
+ try
+ {
+ # Disable Zone checking to prevent warnings when running executables.
+ [System.Environment]::SetEnvironmentVariable('SEE_MASK_NOZONECHECKS', 1)
+
+ # Define process.
+ $process = [System.Diagnostics.Process]@{
+ StartInfo = [System.Diagnostics.ProcessStartInfo]@{
+ FileName = $FilePath
+ WorkingDirectory = $WorkingDirectory
+ UseShellExecute = $UseShellExecute
+ ErrorDialog = $false
+ RedirectStandardOutput = $true
+ RedirectStandardError = $true
+ CreateNoWindow = $CreateNoWindow
+ WindowStyle = $WindowStyle
+ }
+ }
+ if ($ArgumentList)
+ {
+ $process.StartInfo.Arguments = $ArgumentList
+ }
+ if ($process.StartInfo.UseShellExecute)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'UseShellExecute is set to true, standard output and error will not be available.'
+ $process.StartInfo.RedirectStandardOutput = $false
+ $process.StartInfo.RedirectStandardError = $false
+ }
+ else
+ {
+ # Add event handler to capture process's standard output redirection.
+ $processEventHandler = { $Event.MessageData.AppendLine($(if (![System.String]::IsNullOrWhiteSpace($EventArgs.Data)) { $EventArgs.Data })) }
+ $stdOutEvent = & $Script:CommandTable.'Register-ObjectEvent' -InputObject $process -Action $processEventHandler -EventName OutputDataReceived -MessageData $stdOutBuilder
+ $stdErrEvent = & $Script:CommandTable.'Register-ObjectEvent' -InputObject $process -Action $processEventHandler -EventName ErrorDataReceived -MessageData $stdErrBuilder
+ }
+
+ # Start Process.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Working Directory is [$WorkingDirectory]."
+ if ($ArgumentList)
+ {
+ if ($SecureArgumentList)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Executing [$FilePath (Parameters Hidden)]..."
+ }
+ elseif ($ArgumentList -match '-Command \&')
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Executing [$FilePath [PowerShell ScriptBlock]]..."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Executing [$FilePath $ArgumentList]..."
+ }
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Executing [$FilePath]..."
+ }
+ $null = $process.Start()
+
+ # Set priority
+ if ($PriorityClass -ne 'Normal')
+ {
+ try
+ {
+ if (!$process.HasExited)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Changing the priority class for the process to [$PriorityClass]"
+ $process.PriorityClass = $PriorityClass
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Cannot change the priority class for the process to [$PriorityClass], because the process has exited already." -Severity 2
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Failed to change the priority class for the process.' -Severity 2
+ }
+ }
+
+ # NoWait specified, return process details. If it isn't specified, start reading standard Output and Error streams.
+ if ($NoWait)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'NoWait parameter specified. Continuing without waiting for exit code...'
+ if ($PassThru)
+ {
+ if (!$process.HasExited)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'PassThru parameter specified, returning process details object.'
+ $PSCmdlet.WriteObject([PSADT.Types.ProcessInfo]::new(
+ $process.Id,
+ $process.Handle,
+ $process.ProcessName
+ ))
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'PassThru parameter specified, however the process has already exited.'
+ }
+ }
+ }
+ else
+ {
+ # Read all streams to end and wait for the process to exit.
+ if (!$process.StartInfo.UseShellExecute)
+ {
+ $process.BeginOutputReadLine()
+ $process.BeginErrorReadLine()
+ }
+ $process.WaitForExit()
+
+ # HasExited indicates that the associated process has terminated, either normally or abnormally. Wait until HasExited returns $true.
+ while (!$process.HasExited)
+ {
+ $process.Refresh()
+ [System.Threading.Thread]::Sleep(1000)
+ }
+
+ # Get the exit code for the process.
+ $returnCode = $process.ExitCode
+
+ # Process all streams.
+ if (!$process.StartInfo.UseShellExecute)
+ {
+ # Unregister standard output and error event to retrieve process output.
+ if ($stdOutEvent)
+ {
+ & $Script:CommandTable.'Unregister-Event' -SourceIdentifier $stdOutEvent.Name
+ $stdOutEvent = $null
+ }
+ if ($stdErrEvent)
+ {
+ & $Script:CommandTable.'Unregister-Event' -SourceIdentifier $stdErrEvent.Name
+ $stdErrEvent = $null
+ }
+ $stdOut = $stdOutBuilder.ToString().Trim()
+ $stdErr = $stdErrBuilder.ToString().Trim()
+ if (![System.String]::IsNullOrWhiteSpace($stdErr))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Standard error output from the process: $stdErr" -Severity 3
+ }
+ }
+ }
+ }
+ catch
+ {
+ throw
+ }
+ finally
+ {
+ # Make sure the standard output and error event is unregistered.
+ if ($process.StartInfo.UseShellExecute -eq $false)
+ {
+ if ($stdOutEvent)
+ {
+ & $Script:CommandTable.'Unregister-Event' -SourceIdentifier $stdOutEvent.Name -ErrorAction Ignore
+ $stdOutEvent = $null
+ }
+ if ($stdErrEvent)
+ {
+ & $Script:CommandTable.'Unregister-Event' -SourceIdentifier $stdErrEvent.Name -ErrorAction Ignore
+ $stdErrEvent = $null
+ }
+ }
+
+ # Free resources associated with the process, this does not cause process to exit.
+ if ($process)
+ {
+ $process.Dispose()
+ }
+
+ # Re-enable zone checking.
+ [System.Environment]::SetEnvironmentVariable('SEE_MASK_NOZONECHECKS', $null)
+ }
+
+ if (!$NoWait)
+ {
+ # Open variable to store the error message if we failed as we need it when we're determining whether we throw or not.
+ $errorMessage = $null
+
+ # Check to see whether we should ignore exit codes.
+ if (($ignoreExitCode = $IgnoreExitCodes -and ($($IgnoreExitCodes).Equals('*') -or ([System.Int32[]]$IgnoreExitCodes).Contains($returnCode))))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Execution completed and the exit code [$returnCode] is being ignored."
+ }
+ elseif ($RebootExitCodes.Contains($returnCode))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Execution completed successfully with exit code [$returnCode]. A reboot is required." -Severity 2
+ }
+ elseif (($returnCode -eq 1605) -and ($FilePath -match 'msiexec'))
+ {
+ $errorMessage = "Execution failed with exit code [$returnCode] because the product is not currently installed."
+ }
+ elseif (($returnCode -eq -2145124329) -and ($FilePath -match 'wusa'))
+ {
+ $errorMessage = "Execution failed with exit code [$returnCode] because the Windows Update is not applicable to this system."
+ }
+ elseif (($returnCode -eq 17025) -and ($FilePath -match 'fullfile'))
+ {
+ $errorMessage = "Execution failed with exit code [$returnCode] because the Office Update is not applicable to this system."
+ }
+ elseif ($SuccessExitCodes.Contains($returnCode))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Execution completed successfully with exit code [$returnCode]." -Severity 0
+ }
+ else
+ {
+ if (($MsiExitCodeMessage = if ($FilePath -match 'msiexec') { & $Script:CommandTable.'Get-ADTMsiExitCodeMessage' -MsiExitCode $returnCode }))
+ {
+ $errorMessage = "Execution failed with exit code [$returnCode]: $MsiExitCodeMessage"
+ }
+ else
+ {
+ $errorMessage = "Execution failed with exit code [$returnCode]."
+ }
+ }
+
+ # Generate and store the PassThru data.
+ $passthruObj = [PSADT.Types.ProcessResult]::new(
+ $returnCode,
+ $(if (![System.String]::IsNullOrWhiteSpace($stdOut)) { $stdOut }),
+ $(if (![System.String]::IsNullOrWhiteSpace($stdErr)) { $stdErr })
+ )
+
+ # If we have an error in our process, throw it and let the catch block handle it.
+ if ($errorMessage)
+ {
+ $naerParams = @{
+ Exception = [System.ApplicationException]::new($errorMessage)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'ProcessExitCodeError'
+ TargetObject = $passthruObj
+ RecommendedAction = "Please review the exit code with the vendor's documentation and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+
+ # Update the session's last exit code with the value if externally called.
+ if ($adtSession -and $extInvoker -and !$ignoreExitCode)
+ {
+ $adtSession.SetExitCode($returnCode)
+ }
+
+ # If the passthru switch is specified, return the exit code and any output from process.
+ if ($PassThru)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'PassThru parameter specified, returning execution results object.'
+ $PSCmdlet.WriteObject($passthruObj)
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Set up parameters for Invoke-ADTFunctionErrorHandler.
+ if ($null -ne $returnCode)
+ {
+ # Update the session's last exit code with the value if externally called.
+ if ($adtSession -and $extInvoker -and ($OriginalErrorAction -notmatch '^(SilentlyContinue|Ignore)$'))
+ {
+ $adtSession.SetExitCode($returnCode)
+ }
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage $_.Exception.Message -DisableErrorResolving
+ }
+ else
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Error occurred while attempting to start the specified process."
+ }
+
+ # If the passthru switch is specified, return the exit code and any output from process.
+ if ($PassThru)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'PassThru parameter specified, returning execution results object.'
+ $PSCmdlet.WriteObject($_.TargetObject)
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Start-ADTProcessAsUser
+#
+#-----------------------------------------------------------------------------
+
+function Start-ADTProcessAsUser
+{
+ <#
+ .SYNOPSIS
+ Invokes a process in another user's session.
+
+ .DESCRIPTION
+ Invokes a process from SYSTEM in another user's session.
+
+ .PARAMETER FilePath
+ Path to the executable to invoke.
+
+ .PARAMETER ArgumentList
+ Arguments for the invoked executable.
+
+ .PARAMETER WorkingDirectory
+ The 'start-in' directory for the invoked executable.
+
+ .PARAMETER HideWindow
+ Specifies whether the window should be hidden or not.
+
+ .PARAMETER ProcessCreationFlags
+ One or more flags to control the process's invocation.
+
+ .PARAMETER InheritEnvironmentVariables
+ Specifies whether the process should inherit the user's environment state.
+
+ .PARAMETER Wait
+ Specifies whether to wait for the invoked excecutable to finish.
+
+ .PARAMETER Username
+ The username of the user's session to invoke the executable in.
+
+ .PARAMETER SessionId
+ The session ID of the user to invoke the executable in.
+
+ .PARAMETER AllActiveUserSessions
+ Specifies that the executable should be invoked in all active sessions.
+
+ .PARAMETER UseLinkedAdminToken
+ Specifies that an admin token (if available) should be used for the invocation.
+
+ .PARAMETER SuccessExitCodes
+ Specifies one or more exit codes that the function uses to consider the invocation successful.
+
+ .PARAMETER ConsoleTimeoutInSeconds
+ Specifies the timeout in seconds to wait for a console application to finish its task.
+
+ .PARAMETER IsGuiApplication
+ Indicates that the executed application is a GUI-based app, not a console-based app.
+
+ .PARAMETER NoRedirectOutput
+ Specifies that stdout/stderr output should not be redirected to file.
+
+ .PARAMETER MergeStdErrAndStdOut
+ Specifies that the stdout/stderr streams should be merged into a single output.
+
+ .PARAMETER OutputDirectory
+ Specifies the output directory for the redirected stdout/stderr streams.
+
+ .PARAMETER NoTerminateOnTimeout
+ Specifies that the process shouldn't terminate on timeout.
+
+ .PARAMETER AdditionalEnvironmentVariables
+ Specifies additional environment variables to inject into the user's session.
+
+ .PARAMETER WaitOption
+ Specifies the wait type to use when waiting for an invoked executable to finish.
+
+ .PARAMETER SecureArgumentList
+ Hides all parameters passed to the executable from the Toolkit log file.
+
+ .PARAMETER PassThru
+ If NoWait is not specified, returns an object with ExitCode, STDOut and STDErr output from the process. If NoWait is specified, returns an object with Id, Handle and ProcessName.
+
+ .EXAMPLE
+ Start-ADTProcessAsUser -FilePath "$($adtSession.DirFiles)\setup.exe" -ArgumentList '/S' -SuccessExitCodes 0, 500
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Threading.Tasks.Task[System.Int32]
+
+ Returns a task object indicating the process's result.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Start-ADTProcessAsUser
+ #>
+
+ [CmdletBinding(DefaultParameterSetName = 'PrimaryActiveUserSession')]
+ [OutputType([System.Threading.Tasks.Task[System.Int32]])]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $true, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$FilePath,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$ArgumentList,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$WorkingDirectory,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$HideWindow,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.PInvoke.CREATE_PROCESS]$ProcessCreationFlags,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$InheritEnvironmentVariables,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$Wait,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'Username')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Username,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'SessionId')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$SessionId,
+
+ [Parameter(Mandatory = $true, ParameterSetName = 'AllActiveUserSessions')]
+ [System.Management.Automation.SwitchParameter]$AllActiveUserSessions,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$UseLinkedAdminToken,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.Int32[]]$SuccessExitCodes,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.UInt32]$ConsoleTimeoutInSeconds,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$IsGuiApplication,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$NoRedirectOutput,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$MergeStdErrAndStdOut,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$OutputDirectory,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$NoTerminateOnTimeout,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [System.Collections.IDictionary]$AdditionalEnvironmentVariables,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.ProcessEx.WaitType]$WaitOption,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$SecureArgumentList,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Username')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'SessionId')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'AllActiveUserSessions')]
+ [Parameter(Mandatory = $false, ParameterSetName = 'PrimaryActiveUserSession')]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ # Initialise function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Strip out parameters not destined for the C# code.
+ $null = ('SecureArgumentList', 'PassThru').ForEach({
+ if ($PSBoundParameters.ContainsKey($_))
+ {
+ $PSBoundParameters.Remove($_)
+ }
+ })
+
+ # If we're on the default parameter set, pass the right parameter through.
+ if ($PSCmdlet.ParameterSetName.Equals('PrimaryActiveUserSession'))
+ {
+ $PSBoundParameters.Add('PrimaryActiveUserSession', [System.Management.Automation.SwitchParameter]$true)
+ }
+ elseif ($PSBoundParameters.ContainsKey('Username'))
+ {
+ if (!($userSessionId = & $Script:CommandTable.'Get-ADTLoggedOnUser' | & { process { if ($_ -and $_.NTAccount.EndsWith($Username, [System.StringComparison]::InvariantCultureIgnoreCase)) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1 -ExpandProperty SessionId))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName Username -ProvidedValue $Username -ExceptionMessage 'An active session could not be found for the specified user.'))
+ }
+ $PSBoundParameters.Add('SessionId', ($SessionId = $userSessionId))
+ $null = $PSBoundParameters.Remove('Username')
+ }
+
+ # Translate the environment variables into a dictionary. Using this type on the parameter is too hard on the caller.
+ if ($PSBoundParameters.ContainsKey('AdditionalEnvironmentVariables'))
+ {
+ $AdditionalEnvironmentVariables = [System.Collections.Generic.Dictionary[System.String, System.String]]::new()
+ $PSBoundParameters.AdditionalEnvironmentVariables.GetEnumerator() | & {
+ process
+ {
+ $AdditionalEnvironmentVariables.Add($_.Key, $_.Value)
+ }
+ }
+ $PSBoundParameters.AdditionalEnvironmentVariables = $AdditionalEnvironmentVariables
+ }
+
+ # Translate switches that require negation for the LaunchOptions.
+ $null = ('RedirectOutput', 'TerminateOnTimeout').Where({ $PSBoundParameters.ContainsKey("No$_") }).ForEach({
+ $PSBoundParameters.$_ = !$PSBoundParameters."No$_"
+ $PSBoundParameters.Remove("No$_")
+ })
+
+ # Unless explicitly provided, don't terminate on timeout.
+ if (!$PSBoundParameters.ContainsKey('TerminateOnTimeout'))
+ {
+ $PSBoundParameters.TerminateOnTimeout = $false
+ }
+
+ # Translate the process flags into a list of flags. No idea why the backend is coded like this...
+ if ($PSBoundParameters.ContainsKey('ProcessCreationFlags'))
+ {
+ $PSBoundParameters.ProcessCreationFlags = $PSBoundParameters.ProcessCreationFlags.ToString().Split(',').Trim()
+ }
+ }
+
+ process
+ {
+ # Announce start.
+ switch ($PSCmdlet.ParameterSetName)
+ {
+ Username
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invoking [$FilePath$(if ($ArgumentList -and !$SecureArgumentList) { " $ArgumentList" })] as user [$Username]$(if ($Wait) { ", and waiting for invocation to finish" })."
+ break
+ }
+ SessionId
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invoking [$FilePath$(if ($ArgumentList -and !$SecureArgumentList) { " $ArgumentList" })] for session [$SessionId]$(if ($Wait) { ", and waiting for invocation to finish" })."
+ break
+ }
+ AllActiveUserSessions
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invoking [$FilePath$(if ($ArgumentList -and !$SecureArgumentList) { " $ArgumentList" })] for all active user sessions$(if ($Wait) { ", and waiting for all invocations to finish" })."
+ break
+ }
+ PrimaryActiveUserSession
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invoking [$FilePath$(if ($ArgumentList -and !$SecureArgumentList) { " $ArgumentList" })] for the primary user session$(if ($Wait) { ", and waiting for invocation to finish" })."
+ break
+ }
+ }
+
+ # Create a new process object and invoke an execution.
+ try
+ {
+ try
+ {
+ if (($result = ($process = [PSADT.ProcessEx.StartProcess]::new()).ExecuteAndMonitorAsync($PSBoundParameters)) -and $PassThru)
+ {
+ return $result
+ }
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ finally
+ {
+ # Dispose of the process object to ensure things are cleaned up properly.
+ $process.Dispose()
+ }
+ }
+
+ end
+ {
+ # Finalise function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Start-ADTServiceAndDependencies
+#
+#-----------------------------------------------------------------------------
+
+function Start-ADTServiceAndDependencies
+{
+ <#
+ .SYNOPSIS
+ Start a Windows service and its dependencies.
+
+ .DESCRIPTION
+ This function starts a specified Windows service and its dependencies. It provides options to skip starting dependent services, wait for a service to get out of a pending state, and return the service object.
+
+ .PARAMETER Name
+ Specify the name of the service.
+
+ .PARAMETER SkipDependentServices
+ Choose to skip checking for and starting dependent services.
+
+ .PARAMETER PendingStatusWait
+ The amount of time to wait for a service to get out of a pending state before continuing. Default is 60 seconds.
+
+ .PARAMETER PassThru
+ Return the System.ServiceProcess.ServiceController service object.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.ServiceProcess.ServiceController
+
+ Returns the service object.
+
+ .EXAMPLE
+ Start-ADTServiceAndDependencies -Name 'wuauserv'
+
+ Starts the Windows Update service and its dependencies.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Start-ADTServiceAndDependencies
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Service')]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipDependentServices,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$PendingStatusWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTServiceAndDependencyOperation' -Operation Start @PSBoundParameters
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to start the service [$($Service.Name)]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Stop-ADTServiceAndDependencies
+#
+#-----------------------------------------------------------------------------
+
+function Stop-ADTServiceAndDependencies
+{
+ <#
+ .SYNOPSIS
+ Stop a Windows service and its dependencies.
+
+ .DESCRIPTION
+ This function stops a specified Windows service and its dependencies. It provides options to skip stopping dependent services, wait for a service to get out of a pending state, and return the service object.
+
+ .PARAMETER Name
+ Specify the name of the service.
+
+ .PARAMETER SkipDependentServices
+ Choose to skip checking for and stopping dependent services.
+
+ .PARAMETER PendingStatusWait
+ The amount of time to wait for a service to get out of a pending state before continuing. Default is 60 seconds.
+
+ .PARAMETER PassThru
+ Return the System.ServiceProcess.ServiceController service object.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.ServiceProcess.ServiceController
+
+ Returns the service object.
+
+ .EXAMPLE
+ Stop-ADTServiceAndDependencies -Name 'wuauserv'
+
+ Stops the Windows Update service and its dependencies.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Stop-ADTServiceAndDependencies
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [Alias('Service')]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SkipDependentServices,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$PendingStatusWait,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTServiceAndDependencyOperation' -Operation Stop @PSBoundParameters
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to stop the service [$($Service.Name)]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTBattery
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTBattery
+{
+ <#
+ .SYNOPSIS
+ Tests whether the local machine is running on AC power or not.
+
+ .DESCRIPTION
+ Tests whether the local machine is running on AC power and returns true/false. For detailed information, use the -PassThru option to get a hashtable containing various battery and power status properties.
+
+ .PARAMETER PassThru
+ Outputs an object containing the following properties:
+
+ - IsLaptop
+ - IsUsingACPower
+ - ACPowerLineStatus
+ - BatteryChargeStatus
+ - BatteryLifePercent
+ - BatteryLifeRemaining
+ - BatteryFullLifetime
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ PSADT.Types.BatteryInfo
+
+ Returns an object containing the following properties:
+
+ - IsLaptop
+ - IsUsingACPower
+ - ACPowerLineStatus
+ - BatteryChargeStatus
+ - BatteryLifePercent
+ - BatteryLifeRemaining
+ - BatteryFullLifetime
+
+ .EXAMPLE
+ Test-ADTBattery
+
+ Checks if the local machine is running on AC power and returns true or false.
+
+ .EXAMPLE
+ (Test-ADTBattery -PassThru).IsLaptop
+
+ Returns true if the current system is a laptop, otherwise false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTBattery
+ #>
+
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.BatteryInfo])]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Checking if system is using AC power or if it is running on battery...'
+ try
+ {
+ try
+ {
+ # Get the system power status. Indicates whether the system is using AC power or if the status is unknown. Possible values:
+ # Offline : The system is not using AC power.
+ # Online : The system is using AC power.
+ # Unknown : The power status of the system is unknown.
+ $acPowerLineStatus = [System.Windows.Forms.SystemInformation]::PowerStatus.PowerLineStatus
+
+ # Get the current battery charge status. Possible values: High, Low, Critical, Charging, NoSystemBattery, Unknown.
+ $batteryChargeStatus = [System.Windows.Forms.SystemInformation]::PowerStatus.BatteryChargeStatus
+ $invalidBattery = ($batteryChargeStatus -eq 'NoSystemBattery') -or ($batteryChargeStatus -eq 'Unknown')
+
+ # Get the approximate amount, from 0.00 to 1.0, of full battery charge remaining.
+ # This property can report 1.0 when the battery is damaged and Windows can't detect a battery.
+ # Therefore, this property is only indicative of battery charge remaining if 'BatteryChargeStatus' property is not reporting 'NoSystemBattery' or 'Unknown'.
+ $batteryLifePercent = [System.Windows.Forms.SystemInformation]::PowerStatus.BatteryLifePercent * !$invalidBattery
+
+ # The reported approximate number of seconds of battery life remaining. It will report -1 if the remaining life is unknown because the system is on AC power.
+ $batteryLifeRemainingSeconds = [System.Windows.Forms.SystemInformation]::PowerStatus.BatteryLifeRemaining
+
+ # Get the manufacturer reported full charge lifetime of the primary battery power source in seconds.
+ # The reported number of seconds of battery life available when the battery is fully charged, or -1 if it is unknown.
+ # This will only be reported if the battery supports reporting this information. You will most likely get -1, indicating unknown.
+ $batteryFullLifetimeSeconds = [System.Windows.Forms.SystemInformation]::PowerStatus.BatteryFullLifetime
+
+ # Determine if the system is using AC power.
+ $isUsingAcPower = switch ($acPowerLineStatus)
+ {
+ Online
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'System is using AC power.'
+ $true
+ break
+ }
+ Offline
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'System is using battery power.'
+ $false
+ break
+ }
+ Unknown
+ {
+ if ($invalidBattery)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "System power status is [$($acPowerLineStatus)] and battery charge status is [$batteryChargeStatus]. This is most likely due to a damaged battery so we will report system is using AC power."
+ $true
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "System power status is [$($acPowerLineStatus)] and battery charge status is [$batteryChargeStatus]. Therefore, we will report system is using battery power."
+ $false
+ }
+ break
+ }
+ }
+
+ # Determine if the system is a laptop.
+ $isLaptop = !$invalidBattery -and ((& $Script:CommandTable.'Get-CimInstance' -ClassName Win32_SystemEnclosure).ChassisTypes -match '^(9|10|14)$')
+
+ # Return the object if we're passing through, otherwise just whether we're on AC.
+ if ($PassThru)
+ {
+ return [PSADT.Types.BatteryInfo]::new(
+ $acPowerLineStatus,
+ $batteryChargeStatus,
+ $batteryLifePercent,
+ $batteryLifeRemainingSeconds,
+ $batteryFullLifetimeSeconds,
+ $isUsingAcPower,
+ $isLaptop
+ )
+ }
+ return $isUsingAcPower
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTCallerIsAdmin
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTCallerIsAdmin
+{
+ <#
+ .SYNOPSIS
+ Checks if the current user has administrative privileges.
+
+ .DESCRIPTION
+ This function checks if the current user is a member of the Administrators group. It returns a boolean value indicating whether the user has administrative privileges.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the current user is an administrator, otherwise $false.
+
+ .EXAMPLE
+ Test-ADTCallerIsAdmin
+
+ Checks if the current user has administrative privileges and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTCallerIsAdmin
+ #>
+
+ return [System.Security.Principal.WindowsPrincipal]::new([System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltinRole]::Administrator)
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTMicrophoneInUse
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTMicrophoneInUse
+{
+ <#
+ .SYNOPSIS
+ Tests whether the device's microphone is in use.
+
+ .DESCRIPTION
+ Tests whether someone is using the microphone on their device. This could be within Teams, Zoom, a game, or any other app that uses a microphone.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the microphone is in use, otherwise returns $false.
+
+ .EXAMPLE
+ Test-ADTMicrophoneInUse
+
+ Checks if the microphone is in use and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTMicrophoneInUse
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Checking whether the device's microphone is in use..."
+ try
+ {
+ try
+ {
+ if (($microphoneInUse = [PSADT.Devices.Audio]::IsMicrophoneInUse()))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The device's microphone is currently in use."
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "The device's microphone is currently not in use."
+ }
+ return $microphoneInUse
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTModuleInitialized
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTModuleInitialized
+{
+ <#
+ .SYNOPSIS
+ Checks if the ADT (PSAppDeployToolkit) module is initialized.
+
+ .DESCRIPTION
+ This function checks if the ADT (PSAppDeployToolkit) module is initialized by retrieving the module data and returning the initialization status.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the ADT module is initialized, otherwise $false.
+
+ .EXAMPLE
+ Test-ADTModuleInitialized
+
+ Checks if the ADT module is initialized and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTModuleInitialized
+ #>
+
+ return $Script:ADT.Initialized
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTMSUpdates
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTMSUpdates
+{
+ <#
+ .SYNOPSIS
+ Test whether a Microsoft Windows update is installed.
+
+ .DESCRIPTION
+ This function checks if a specified Microsoft Windows update, identified by its KB number, is installed on the local machine. It first attempts to find the update using the Get-HotFix cmdlet and, if unsuccessful, uses a COM object to search the update history.
+
+ .PARAMETER KbNumber
+ KBNumber of the update.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the update is installed, otherwise returns $false.
+
+ .EXAMPLE
+ Test-ADTMSUpdates -KBNumber 'KB2549864'
+
+ Checks if the Microsoft Update 'KB2549864' is installed and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTMSUpdates
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, HelpMessage = 'Enter the KB Number for the Microsoft Update')]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$KbNumber
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Checking if Microsoft Update [$KbNumber] is installed."
+ try
+ {
+ try
+ {
+ # Attempt to get the update via Get-HotFix first as it's cheap.
+ if (!($kbFound = !!(& $Script:CommandTable.'Get-HotFix' -Id $KbNumber -ErrorAction Ignore)))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Unable to detect Windows update history via Get-Hotfix cmdlet. Trying via COM object.'
+ $updateSearcher = (& $Script:CommandTable.'New-Object' -ComObject Microsoft.Update.Session).CreateUpdateSearcher()
+ $updateSearcher.IncludePotentiallySupersededUpdates = $false
+ $updateSearcher.Online = $false
+ if (($updateHistoryCount = $updateSearcher.GetTotalHistoryCount()) -gt 0)
+ {
+ $kbFound = !!($updateSearcher.QueryHistory(0, $updateHistoryCount) | & { process { if (($_.Operation -ne 'Other') -and ($_.Title -match "\($KBNumber\)") -and ($_.Operation -eq 1) -and ($_.ResultCode -eq 2)) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1)
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Unable to detect Windows Update history via COM object.'
+ return
+ }
+ }
+
+ # Return result.
+ if ($kbFound)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Microsoft Update [$KbNumber] is installed."
+ return $true
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Microsoft Update [$KbNumber] is not installed."
+ return $false
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed discovering Microsoft Update [$kbNumber]."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTMutexAvailability
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTMutexAvailability
+{
+ <#
+ .SYNOPSIS
+ Wait, up to a timeout value, to check if current thread is able to acquire an exclusive lock on a system mutex.
+
+ .DESCRIPTION
+ A mutex can be used to serialize applications and prevent multiple instances from being opened at the same time.
+
+ Wait, up to a timeout (default is 1 millisecond), for the mutex to become available for an exclusive lock.
+
+ .PARAMETER MutexName
+ The name of the system mutex.
+
+ .PARAMETER MutexWaitTime
+ The number of milliseconds the current thread should wait to acquire an exclusive lock of a named mutex.
+
+ A wait time of -1 milliseconds means to wait indefinitely. A wait time of zero does not acquire an exclusive lock but instead tests the state of the wait handle and returns immediately.
+
+ .INPUTS
+ None. You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean. Returns $true if the current thread acquires an exclusive lock on the named mutex, $false otherwise.
+
+ .EXAMPLE
+ Test-ADTMutexAvailability -MutexName 'Global\_MSIExecute' -MutexWaitTime 5000000
+
+ .EXAMPLE
+ Test-ADTMutexAvailability -MutexName 'Global\_MSIExecute' -MutexWaitTime (New-TimeSpan -Minutes 5)
+
+ .EXAMPLE
+ Test-ADTMutexAvailability -MutexName 'Global\_MSIExecute' -MutexWaitTime (New-TimeSpan -Seconds 60)
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ http://msdn.microsoft.com/en-us/library/aa372909(VS.85).asp
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTMutexAvailability
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateLength(1, 260)]
+ [System.String]$MutexName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.TimeSpan]$MutexWaitTime = [System.TimeSpan]::FromMilliseconds(1)
+ )
+
+ begin
+ {
+ # Initialize variables.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $WaitLogMsg = if ($MutexWaitTime.TotalMinutes -ge 1)
+ {
+ "$($MutexWaitTime.TotalMinutes) minute(s)"
+ }
+ elseif ($MutexWaitTime.TotalSeconds -ge 1)
+ {
+ "$($MutexWaitTime.TotalSeconds) second(s)"
+ }
+ else
+ {
+ "$($MutexWaitTime.Milliseconds) millisecond(s)"
+ }
+ $IsUnhandledException = $false
+ $IsMutexFree = $false
+ [System.Threading.Mutex]$OpenExistingMutex = $null
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Checking to see if mutex [$MutexName] is available. Wait up to [$WaitLogMsg] for the mutex to become available."
+ try
+ {
+ # Open the specified named mutex, if it already exists, without acquiring an exclusive lock on it. If the system mutex does not exist, this method throws an exception instead of creating the system object.
+ $OpenExistingMutex = [System.Threading.Mutex]::OpenExisting($MutexName)
+
+ # Attempt to acquire an exclusive lock on the mutex. Use a Timespan to specify a timeout value after which no further attempt is made to acquire a lock on the mutex.
+ $IsMutexFree = $OpenExistingMutex.WaitOne($MutexWaitTime, $false)
+ }
+ catch [Threading.WaitHandleCannotBeOpenedException]
+ {
+ # The named mutex does not exist.
+ $IsMutexFree = $true
+ }
+ catch [ObjectDisposedException]
+ {
+ # Mutex was disposed between opening it and attempting to wait on it.
+ $IsMutexFree = $true
+ }
+ catch [UnauthorizedAccessException]
+ {
+ # The named mutex exists, but the user does not have the security access required to use it.
+ $IsMutexFree = $false
+ }
+ catch [Threading.AbandonedMutexException]
+ {
+ # The wait completed because a thread exited without releasing a mutex. This exception is thrown when one thread acquires a mutex object that another thread has abandoned by exiting without releasing it.
+ $IsMutexFree = $true
+ }
+ catch
+ {
+ # Return $true, to signify that mutex is available, because function was unable to successfully complete a check due to an unhandled exception. Default is to err on the side of the mutex being available on a hard failure.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Unable to check if mutex [$MutexName] is available due to an unhandled exception. Will default to return value of [$true].`n$(& $Script:CommandTable.'Resolve-ADTErrorRecord' -ErrorRecord $_)" -Severity 3
+ $IsUnhandledException = $true
+ $IsMutexFree = $true
+ }
+ finally
+ {
+ if ($IsMutexFree)
+ {
+ if (!$IsUnhandledException)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Mutex [$MutexName] is available for an exclusive lock."
+ }
+ }
+ elseif (($MutexName -eq 'Global\_MSIExecute') -and ($msiInProgressCmdLine = & $Script:CommandTable.'Get-CimInstance' -ClassName Win32_Process -Filter "(Name = 'msiexec.exe') AND (CommandLine like '*.msi*')" | & $Script:CommandTable.'Select-Object' -ExpandProperty CommandLine))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Mutex [$MutexName] is not available for an exclusive lock because the following MSI installation is in progress [$($msiInProgressCmdLine.Trim())]." -Severity 2
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Mutex [$MutexName] is not available because another thread already has an exclusive lock on it."
+ }
+
+ if (($null -ne $OpenExistingMutex) -and $IsMutexFree)
+ {
+ # Release exclusive lock on the mutex.
+ $null = $OpenExistingMutex.ReleaseMutex()
+ $OpenExistingMutex.Close()
+ }
+ }
+ return $IsMutexFree
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTNetworkConnection
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTNetworkConnection
+{
+ <#
+ .SYNOPSIS
+ Tests for an active local network connection, excluding wireless and virtual network adapters.
+
+ .DESCRIPTION
+ Tests for an active local network connection, excluding wireless and virtual network adapters, by querying the Win32_NetworkAdapter WMI class. This function checks if any physical network adapter is in the 'Up' status.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if a wired network connection is detected, otherwise returns $false.
+
+ .EXAMPLE
+ Test-ADTNetworkConnection
+
+ Checks if there is an active wired network connection and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTNetworkConnection
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Checking if system is using a wired network connection...'
+ try
+ {
+ try
+ {
+ if (& $Script:CommandTable.'Get-NetAdapter' -Physical | & { process { if ($_.Status.Equals('Up')) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Wired network connection found.'
+ return $true
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Wired network connection not found.'
+ return $false
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTOobeCompleted
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTOobeCompleted
+{
+ <#
+ .SYNOPSIS
+ Checks if the device's Out-of-Box Experience (OOBE) has completed or not.
+
+ .DESCRIPTION
+ This function checks if the current device has completed the Out-of-Box Experience (OOBE).
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the device has proceeded past the OOBE, otherwise $false.
+
+ .EXAMPLE
+ Test-ADTOobeCompleted
+
+ Checks if the device has completed the OOBE or not and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTOobeCompleted
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ )
+
+ begin
+ {
+ # Initialize function.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ # Return whether the OOBE is completed via an API call.
+ try
+ {
+ try
+ {
+ return ([PSADT.Shared.Utility]::IsOOBEComplete())
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Error determining whether the OOBE has been completed or not."
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTPowerPoint
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTPowerPoint
+{
+ <#
+ .SYNOPSIS
+ Tests whether PowerPoint is running in either fullscreen slideshow mode or presentation mode.
+
+ .DESCRIPTION
+ Tests whether someone is presenting using PowerPoint in either fullscreen slideshow mode or presentation mode. This function checks if the PowerPoint process has a window with a title that begins with "PowerPoint Slide Show" or "PowerPoint-" for non-English language systems. There is a possibility of a false positive if the PowerPoint filename starts with "PowerPoint Slide Show". If the previous detection method does not detect PowerPoint in fullscreen mode, it checks if PowerPoint is in Presentation Mode (only works on Windows Vista or higher).
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if PowerPoint is running in either fullscreen slideshow mode or presentation mode, otherwise returns $false.
+
+ .EXAMPLE
+ Test-ADTPowerPoint
+
+ Checks if PowerPoint is running in either fullscreen slideshow mode or presentation mode and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ This function can only execute detection logic if the process is in interactive mode.
+
+ There is a possibility of a false positive if the PowerPoint filename starts with "PowerPoint Slide Show".
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTPowerPoint
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $procName = 'POWERPNT'
+ $presenting = 'Unknown'
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Checking if PowerPoint is in either fullscreen slideshow mode or presentation mode...'
+ try
+ {
+ try
+ {
+ # Return early if we're not running PowerPoint or we can't interactively check.
+ if (!($PowerPointProcess = & $Script:CommandTable.'Get-Process' -Name $procName -ErrorAction Ignore))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'PowerPoint application is not running.'
+ return ($presenting = $false)
+ }
+ if (![System.Environment]::UserInteractive)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Unable to run check to see if PowerPoint is in fullscreen mode or Presentation Mode because current process is not interactive. Configure script to run in interactive mode in your deployment tool. If using SCCM Application Model, then make sure "Allow users to view and interact with the program installation" is selected. If using SCCM Package Model, then make sure "Allow users to interact with this program" is selected.' -Severity 2
+ return
+ }
+
+ # Check if "POWERPNT" process has a window with a title that begins with "PowerPoint Slide Show" or "Powerpoint-" for non-English language systems.
+ # There is a possiblity of a false positive if the PowerPoint filename starts with "PowerPoint Slide Show".
+ if (& $Script:CommandTable.'Get-ADTWindowTitle' -GetAllWindowTitles | & { process { if (($_.ParentProcess -eq $procName) -and ($_.WindowTitle -match '^PowerPoint(-| Slide Show)')) { return $_ } } } | & $Script:CommandTable.'Select-Object' -First 1)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Detected that PowerPoint process [$procName] has a window with a title that beings with [PowerPoint Slide Show] or [PowerPoint-]."
+ return ($presenting = $true)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Detected that PowerPoint process [$procName] does not have a window with a title that beings with [PowerPoint Slide Show] or [PowerPoint-]."
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "PowerPoint process [$procName] has process ID(s) [$(($PowerPointProcessIDs = $PowerPointProcess.Id) -join ', ')]."
+
+ # If previous detection method did not detect PowerPoint in fullscreen mode, then check if PowerPoint is in Presentation Mode (check only works on Windows Vista or higher).
+ # Note: The below method does not detect PowerPoint presentation mode if the presentation is on a monitor that does not have current mouse input control.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Detected user notification state [$(($UserNotificationState = [PSADT.GUI.UiAutomation]::GetUserNotificationState()))]."
+ switch ($UserNotificationState)
+ {
+ QUNS_PRESENTATION_MODE
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Detected that system is in [Presentation Mode].'
+ return ($presenting = $true)
+ }
+ QUNS_BUSY
+ {
+ if ($PowerPointProcessIDs -contains [PSADT.GUI.UiAutomation]::GetWindowThreadProcessId([PSADT.LibraryInterfaces.User32]::GetForegroundWindow()))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Detected a fullscreen foreground window matches a PowerPoint process ID.'
+ return ($presenting = $true)
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Unable to find a fullscreen foreground window that matches a PowerPoint process ID.'
+ break
+ }
+ }
+ return ($presenting = $false)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "PowerPoint is running in fullscreen mode [$presenting]."
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTRegistryValue
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTRegistryValue
+{
+ <#
+ .SYNOPSIS
+ Test if a registry value exists.
+
+ .DESCRIPTION
+ Checks a registry key path to see if it has a value with a given name. Can correctly handle cases where a value simply has an empty or null value.
+
+ .PARAMETER Key
+ Path of the registry key.
+
+ .PARAMETER Name
+ Specify the name of the value to check the existence of.
+
+ .PARAMETER SID
+ The security identifier (SID) for a user. Specifying this parameter will convert a HKEY_CURRENT_USER registry key to the HKEY_USERS\$SID format.
+
+ Specify this parameter from the Invoke-ADTAllUsersRegistryAction function to read/edit HKCU registry settings for all users on the system.
+
+ .PARAMETER Wow6432Node
+ Specify this switch to check the 32-bit registry (Wow6432Node) on 64-bit systems.
+
+ .INPUTS
+ System.String
+
+ Accepts a string value for the registry key path.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the registry value exists, $false if it does not.
+
+ .EXAMPLE
+ Test-ADTRegistryValue -Key 'HKLM:SYSTEM\CurrentControlSet\Control\Session Manager' -Name 'PendingFileRenameOperations'
+
+ Checks if the registry value 'PendingFileRenameOperations' exists under the specified key.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ To test if a registry key exists, use the Test-Path function like so: Test-Path -LiteralPath $Key -PathType 'Container'
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTRegistryValue
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Key,
+
+ [Parameter(Mandatory = $true, Position = 1)]
+ [ValidateNotNullOrEmpty()]
+ [System.Object]$Name,
+
+ [Parameter(Mandatory = $false, Position = 2)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$SID,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$Wow6432Node
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # If the SID variable is specified, then convert all HKEY_CURRENT_USER key's to HKEY_USERS\$SID.
+ $Key = if ($PSBoundParameters.ContainsKey('SID'))
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key -Wow6432Node:$Wow6432Node -SID $SID
+ }
+ else
+ {
+ & $Script:CommandTable.'Convert-ADTRegistryPath' -Key $Key -Wow6432Node:$Wow6432Node
+ }
+
+ # Test whether value exists or not.
+ if ((& $Script:CommandTable.'Get-Item' -LiteralPath $Key -ErrorAction Ignore | & $Script:CommandTable.'Select-Object' -ExpandProperty Property -ErrorAction Ignore) -contains $Name)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Registry key value [$Key] [$Name] does exist."
+ return $true
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Registry key value [$Key] [$Name] does not exist."
+ return $false
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTServiceExists
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTServiceExists
+{
+ <#
+ .SYNOPSIS
+ Check to see if a service exists.
+
+ .DESCRIPTION
+ Check to see if a service exists. The UseCIM switch can be used in conjunction with PassThru to return WMI objects for PSADT v3.x compatibility, however, this method fails in Windows Sandbox.
+
+ .PARAMETER Name
+ Specify the name of the service.
+
+ Note: Service name can be found by executing "Get-Service | Format-Table -AutoSize -Wrap" or by using the properties screen of a service in services.msc.
+
+ .PARAMETER UseCIM
+ Use CIM/WMI to check for the service. This is useful for compatibility with PSADT v3.x.
+
+ .PARAMETER PassThru
+ Return the WMI service object. To see all the properties use: Test-ADTServiceExists -Name 'spooler' -PassThru | Get-Member
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the service exists, otherwise returns $false.
+
+ .EXAMPLE
+ Test-ADTServiceExists -Name 'wuauserv'
+
+ Checks if the service 'wuauserv' exists.
+
+ .EXAMPLE
+ Test-ADTServiceExists -Name testservice -UseCIM -PassThru | Invoke-CimMethod -MethodName Delete
+
+ Checks if a service exists and then deletes it by using the -PassThru parameter.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTServiceExists
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = "This function is appropriately named and we don't need PSScriptAnalyzer telling us otherwise.")]
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Name,
+
+ [Parameter(Mandatory = $false)]
+ [Alias('UseWMI')]
+ [System.Management.Automation.SwitchParameter]$UseCIM,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ # Access via CIM/WMI if specifically asked.
+ if ($UseCIM)
+ {
+ # If nothing is returned from Win32_Service, check Win32_BaseService.
+ if (!($ServiceObject = & $Script:CommandTable.'Get-CimInstance' -ClassName Win32_Service -Filter "Name = '$Name'"))
+ {
+ $ServiceObject = & $Script:CommandTable.'Get-CimInstance' -ClassName Win32_BaseService -Filter "Name = '$Name'"
+ }
+ }
+ else
+ {
+ # If the result is empty, it means the provided service is invalid.
+ $ServiceObject = & $Script:CommandTable.'Get-Service' -Name $Name -ErrorAction Ignore
+ }
+
+ # Return early if null.
+ if (!$ServiceObject)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Service [$Name] does not exist."
+ return $false
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Service [$Name] exists."
+
+ # Return the CIM object if passing through.
+ if ($PassThru)
+ {
+ return $ServiceObject
+ }
+ return $true
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed check to see if service [$Name] exists."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTSessionActive
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTSessionActive
+{
+ <#
+ .SYNOPSIS
+ Checks if there is an active ADT session.
+
+ .DESCRIPTION
+ This function checks if there is an active ADT (App Deploy Toolkit) session by retrieving the module data and returning the count of active sessions.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if there is at least one active session, otherwise $false.
+
+ .EXAMPLE
+ Test-ADTSessionActive
+
+ Checks if there is an active ADT session and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTSessionActive
+ #>
+
+ return !!$Script:ADT.Sessions.Count
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Test-ADTUserIsBusy
+#
+#-----------------------------------------------------------------------------
+
+function Test-ADTUserIsBusy
+{
+ <#
+ .SYNOPSIS
+ Tests whether the device's microphone is in use, the user has manually turned on presentation mode, or PowerPoint is running in either fullscreen slideshow mode or presentation mode.
+
+ .DESCRIPTION
+ Tests whether the device's microphone is in use, the user has manually turned on presentation mode, or PowerPoint is running in either fullscreen slideshow mode or presentation mode.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ System.Boolean
+
+ Returns $true if the device's microphone is in use, the user has manually turned on presentation mode, or PowerPoint is running in either fullscreen slideshow mode or presentation mode, otherwise $false.
+
+ .EXAMPLE
+ Test-ADTUserIsBusy
+
+ Tests whether the device's microphone is in use, the user has manually turned on presentation mode, or PowerPoint is running in either fullscreen slideshow mode or presentation mode, and returns true or false.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Test-ADTUserIsBusy
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Boolean])]
+ param
+ (
+ )
+
+ try
+ {
+ return ((& $Script:CommandTable.'Test-ADTMicrophoneInUse') -or (& $Script:CommandTable.'Get-ADTPresentationSettingsEnabledUsers') -or (& $Script:CommandTable.'Test-ADTPowerPoint'))
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Unblock-ADTAppExecution
+#
+#-----------------------------------------------------------------------------
+
+function Unblock-ADTAppExecution
+{
+ <#
+ .SYNOPSIS
+ Unblocks the execution of applications performed by the Block-ADTAppExecution function.
+
+ .DESCRIPTION
+ This function is called by the Close-ADTSession function. It undoes the actions performed by Block-ADTAppExecution, allowing previously blocked applications to execute.
+
+ .PARAMETER Tasks
+ Specify the scheduled tasks to unblock.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not generate any output.
+
+ .EXAMPLE
+ Unblock-ADTAppExecution
+
+ Unblocks the execution of applications that were previously blocked by Block-ADTAppExecution.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ It is used when the -BlockExecution parameter is specified with the Show-ADTInstallationWelcome function to undo the actions performed by Block-ADTAppExecution.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Unblock-ADTAppExecution
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [PSDefaultValue(Help = "All scheduled tasks wildcard matching [PSAppDeployToolkit_*_BlockedApps].")]
+ [Microsoft.Management.Infrastructure.CimInstance[]]$Tasks = (& $Script:CommandTable.'Get-ScheduledTask' -TaskName "$($MyInvocation.MyCommand.Module.Name)_*_BlockedApps" -ErrorAction Ignore)
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ $uaaeiParams = @{}; if ($Tasks) { $uaaeiParams.Add('Tasks', $Tasks) }
+ }
+
+ process
+ {
+ # Bypass if no admin rights.
+ if (!(& $Script:CommandTable.'Test-ADTCallerIsAdmin'))
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Bypassing Function [$($MyInvocation.MyCommand.Name)], because [User: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)] is not admin."
+ return
+ }
+
+ # Clean up blocked apps using our backend worker.
+ try
+ {
+ try
+ {
+ & $Script:CommandTable.'Unblock-ADTAppExecutionInternal' @uaaeiParams -Verbose 4>&1 | & $Script:CommandTable.'Write-ADTLogEntry'
+ & $Script:CommandTable.'Remove-ADTSessionFinishingCallback' -Callback $MyInvocation.MyCommand
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Uninstall-ADTApplication
+#
+#-----------------------------------------------------------------------------
+
+function Uninstall-ADTApplication
+{
+ <#
+ .SYNOPSIS
+ Removes one or more applications specified by name, filter script, or InstalledApplication object from Get-ADTApplication.
+
+ .DESCRIPTION
+ Removes one or more applications specified by name, filter script, or InstalledApplication object from Get-ADTApplication.
+
+ Enumerates the registry for installed applications via Get-ADTApplication, matching the specified application name and uninstalls that application using its uninstall string, with the ability to specify additional uninstall parameters also.
+
+ .PARAMETER InstalledApplication
+ Specifies the [PSADT.Types.InstalledApplication] object to remove. This parameter is typically used when piping Get-ADTApplication to this function.
+
+ .PARAMETER Name
+ The name of the application to retrieve information for. Performs a contains match on the application display name by default.
+
+ .PARAMETER NameMatch
+ Specifies the type of match to perform on the application name. Valid values are 'Contains', 'Exact', 'Wildcard', and 'Regex'. The default value is 'Contains'.
+
+ .PARAMETER ProductCode
+ The product code of the application to retrieve information for.
+
+ .PARAMETER ApplicationType
+ Specifies the type of application to remove. Valid values are 'All', 'MSI', and 'EXE'. The default value is 'All'.
+
+ .PARAMETER IncludeUpdatesAndHotfixes
+ Include matches against updates and hotfixes in results.
+
+ .PARAMETER FilterScript
+ A script used to filter the results as they're processed.
+
+ .PARAMETER ArgumentList
+ Overrides the default MSI parameters specified in the config.psd1 file, or the parameters found in QuietUninstallString/UninstallString for EXE applications.
+
+ .PARAMETER AdditionalArgumentList
+ Adds to the default parameters specified in the config.psd1 file, or the parameters found in QuietUninstallString/UninstallString for EXE applications.
+
+ .PARAMETER SecureArgumentList
+ Hides all parameters passed to the executable from the Toolkit log file.
+
+ .PARAMETER LoggingOptions
+ Overrides the default MSI logging options specified in the config.psd1 file. Default options are: "/L*v".
+
+ .PARAMETER LogFileName
+ Overrides the default log file name for MSI applications. The default log file name is generated from the MSI file name. If LogFileName does not end in .log, it will be automatically appended.
+
+ For uninstallations, by default the product code is resolved to the DisplayName and version of the application.
+
+ .PARAMETER PassThru
+ Returns a PSADT.Types.ProcessResult object, providing the ExitCode, StdOut, and StdErr output from the uninstallation.
+
+ .INPUTS
+ PSADT.Types.InstalledApplication
+
+ This function can receive one or more InstalledApplication objects for uninstallation.
+
+ .OUTPUTS
+ PSADT.Types.ProcessResult
+
+ Returns an object with the results of the installation if -PassThru is specified.
+ - ExitCode
+ - StdOut
+ - StdErr
+
+ .EXAMPLE
+ Uninstall-ADTApplication -Name 'Acrobat' -ApplicationType 'MSI' -FilterScript { $_.Publisher -match 'Adobe' }
+
+ Removes all MSI applications that contain the name 'Acrobat' in the DisplayName and 'Adobe' in the Publisher name.
+
+ .EXAMPLE
+ Uninstall-ADTApplication -Name 'Java' -FilterScript {$_.Publisher -eq 'Oracle Corporation' -and $_.Is64BitApplication -eq $true -and $_.DisplayVersion -notlike '8.*'}
+
+ Removes all MSI applications that contain the name 'Java' in the DisplayName, with Publisher as 'Oracle Corporation', are 64-bit, and not version 8.x.
+
+ .EXAMPLE
+ Uninstall-ADTApplication -FilterScript {$_.DisplayName -match '^Vim\s'} -Verbose -ApplicationType EXE -ArgumentList '/S'
+
+ Remove all EXE applications starting with the name 'Vim' followed by a space, using the '/S' parameter.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ More reading on how to create filterscripts https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/where-object?view=powershell-5.1#description
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Uninstall-ADTApplication
+ #>
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'NameMatch', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'ApplicationType', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'IncludeUpdatesAndHotfixes', Justification = "This parameter is used within delegates that PSScriptAnalyzer has no visibility of. See https://github.com/PowerShell/PSScriptAnalyzer/issues/1472 for more details.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'LoggingOptions', Justification = "This parameter is used/retrieved via Get-ADTBoundParametersAndDefaultValues, which is too advanced for PSScriptAnalyzer to comprehend.")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'LogFileName', Justification = "This parameter is used/retrieved via Get-ADTBoundParametersAndDefaultValues, which is too advanced for PSScriptAnalyzer to comprehend.")]
+ [CmdletBinding()]
+ [OutputType([PSADT.Types.ProcessResult])]
+ [OutputType([PSADT.Types.ProcessInfo])]
+ param
+ (
+ [Parameter(Mandatory = $true, ParameterSetName = 'InstalledApplication', ValueFromPipeline = $true)]
+ [ValidateNotNullOrEmpty()]
+ [PSADT.Types.InstalledApplication[]]$InstalledApplication,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Search')]
+ [ValidateNotNullOrEmpty()]
+ [System.String[]]$Name,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Search')]
+ [ValidateSet('Contains', 'Exact', 'Wildcard', 'Regex')]
+ [System.String]$NameMatch = 'Contains',
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Search')]
+ [ValidateNotNullOrEmpty()]
+ [System.Guid[]]$ProductCode,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Search')]
+ [ValidateSet('All', 'MSI', 'EXE')]
+ [System.String]$ApplicationType = 'All',
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Search')]
+ [System.Management.Automation.SwitchParameter]$IncludeUpdatesAndHotfixes,
+
+ [Parameter(Mandatory = $false, ParameterSetName = 'Search', Position = 0)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.ScriptBlock]$FilterScript,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$AdditionalArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$SecureArgumentList,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LoggingOptions,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LogFileName,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.Management.Automation.SwitchParameter]$PassThru
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+
+ if ($PSCmdlet.ParameterSetName -ne 'InstalledApplication')
+ {
+ if (!($PSBoundParameters.Keys -match '^(Name|ProductCode|FilterScript)$'))
+ {
+ $naerParams = @{
+ Exception = [System.ArgumentNullException]::new('Either Name, ProductCode or FilterScript are required if not using pipeline.')
+ Category = [System.Management.Automation.ErrorCategory]::InvalidArgument
+ ErrorId = 'NullParameterValue'
+ RecommendedAction = "Review the supplied parameter values and try again."
+ }
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTErrorRecord' @naerParams))
+ }
+
+ # Build the hashtable with the options that will be passed to Get-ADTApplication using splatting
+ $gaiaParams = & $Script:CommandTable.'Get-ADTBoundParametersAndDefaultValues' -Invocation $MyInvocation -Exclude ArgumentList, AdditionalArgumentList, LoggingOptions, LogFileName, PassThru, SecureArgumentList
+ if (($installedApps = & $Script:CommandTable.'Get-ADTApplication' @gaiaParams))
+ {
+ $InstalledApplication = $installedApps
+ }
+ }
+
+ # Build the hashtable with the options that will be passed to Start-ADTMsiProcess using splatting
+ $sampParams = & $Script:CommandTable.'Get-ADTBoundParametersAndDefaultValues' -Invocation $MyInvocation -Exclude InstalledApplication, Name, NameMatch, ProductCode, FilterScript, ApplicationType
+ $sampParams.Action = 'Uninstall'
+
+ # Build the hashtable with the options that will be passed to Start-ADTProcess using splatting.
+ $sapParams = @{
+ SecureArgumentList = $SecureArgumentList
+ WaitForMsiExec = $true
+ CreateNoWindow = $true
+ PassThru = $PassThru
+ }
+
+ # Build out regex for determining valid exe uninstall strings.
+ $invalidFileNameChars = [System.Text.RegularExpressions.Regex]::Escape([System.String]::Join($null, [System.IO.Path]::GetInvalidFileNameChars()))
+ $invalidPathChars = [System.Text.RegularExpressions.Regex]::Escape([System.String]::Join($null, [System.IO.Path]::GetInvalidPathChars()))
+ $validUninstallString = "^`"?([^$invalidFileNameChars\s]+(?=\s|$)|[^$invalidPathChars]+?\.(?:exe|cmd|bat|vbs))`"?(?:\s(.*))?$"
+ }
+
+ process
+ {
+ if (!$InstalledApplication)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'No applications found for removal.'
+ return
+ }
+
+ foreach ($removeApplication in $InstalledApplication)
+ {
+ try
+ {
+ if ($removeApplication.WindowsInstaller)
+ {
+ if (!$removeApplication.ProductCode)
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "No ProductCode found for MSI application [$($removeApplication.DisplayName) $($removeApplication.DisplayVersion)]. Skipping removal."
+ continue
+ }
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing MSI application [$($removeApplication.DisplayName) $($removeApplication.DisplayVersion)] with ProductCode [$($removeApplication.ProductCode.ToString('B'))]."
+ try
+ {
+ if ($sampParams.ContainsKey('FilePath'))
+ {
+ $null = $sampParams.Remove('FilePath')
+ }
+ $removeApplication | & $Script:CommandTable.'Start-ADTMsiProcess' @sampParams
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ else
+ {
+ $uninstallString = if (![System.String]::IsNullOrWhiteSpace($removeApplication.QuietUninstallString))
+ {
+ $removeApplication.QuietUninstallString
+ }
+ elseif (![System.String]::IsNullOrWhiteSpace($removeApplication.UninstallString))
+ {
+ $removeApplication.UninstallString
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "No UninstallString found for EXE application [$($removeApplication.DisplayName) $($removeApplication.DisplayVersion)]. Skipping removal."
+ continue
+ }
+
+ if ($uninstallString -match $validUninstallString)
+ {
+ $sapParams.FilePath = [System.Environment]::ExpandEnvironmentVariables($matches[1])
+ if (![System.IO.File]::Exists($sapParams.FilePath) -and ($commandPath = & $Script:CommandTable.'Get-Command' -Name $sapParams.FilePath -ErrorAction Ignore))
+ {
+ $sapParams.FilePath = $commandPath.Source
+ }
+ $uninstallStringParams = if ($matches.Count -gt 2)
+ {
+ [System.Environment]::ExpandEnvironmentVariables($matches[2].Trim())
+ }
+ }
+ else
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Invalid UninstallString [$uninstallString] found for EXE application [$($removeApplication.DisplayName) $($removeApplication.DisplayVersion)]. Skipping removal."
+ continue
+ }
+
+ if (![System.String]::IsNullOrWhiteSpace($ArgumentList))
+ {
+ $sapParams.ArgumentList = $ArgumentList
+ }
+ elseif (![System.String]::IsNullOrWhiteSpace($uninstallStringParams))
+ {
+ $sapParams.ArgumentList = $uninstallStringParams
+ }
+ else
+ {
+ $sapParams.Remove('ArgumentList')
+ }
+ if ($AdditionalArgumentList)
+ {
+ if ($sapParams.ContainsKey('ArgumentList'))
+ {
+ $sapParams.ArgumentList += " $([System.String]::Join(' ', $AdditionalArgumentList))"
+ }
+ else
+ {
+ $sapParams.ArgumentList = $AdditionalArgumentList
+ }
+ }
+
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "Removing EXE application [$($removeApplication.DisplayName) $($removeApplication.DisplayVersion)]."
+ try
+ {
+ & $Script:CommandTable.'Start-ADTProcess' @sapParams
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Unregister-ADTDll
+#
+#-----------------------------------------------------------------------------
+
+function Unregister-ADTDll
+{
+ <#
+ .SYNOPSIS
+ Unregister a DLL file.
+
+ .DESCRIPTION
+ Unregister a DLL file using regsvr32.exe. This function takes the path to the DLL file and attempts to unregister it using the regsvr32.exe utility.
+
+ .PARAMETER FilePath
+ Path to the DLL file.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return objects.
+
+ .EXAMPLE
+ Unregister-ADTDll -FilePath "C:\Test\DcTLSFileToDMSComp.dll"
+
+ Unregisters the specified DLL file.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Unregister-ADTDll
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $true)]
+ [ValidateScript({
+ if (![System.IO.File]::Exists($_))
+ {
+ $PSCmdlet.ThrowTerminatingError((& $Script:CommandTable.'New-ADTValidateScriptErrorRecord' -ParameterName FilePath -ProvidedValue $_ -ExceptionMessage 'The specified file does not exist.'))
+ }
+ return ![System.String]::IsNullOrWhiteSpace($_)
+ })]
+ [System.String]$FilePath
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ & $Script:CommandTable.'Invoke-ADTRegSvr32' @PSBoundParameters -Action Unregister
+ }
+ catch
+ {
+ $PSCmdlet.ThrowTerminatingError($_)
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Update-ADTDesktop
+#
+#-----------------------------------------------------------------------------
+
+function Update-ADTDesktop
+{
+ <#
+ .SYNOPSIS
+ Refresh the Windows Explorer Shell, which causes the desktop icons and the environment variables to be reloaded.
+
+ .DESCRIPTION
+ This function refreshes the Windows Explorer Shell, causing the desktop icons and environment variables to be reloaded. This can be useful after making changes that affect the desktop or environment variables, ensuring that the changes are reflected immediately.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Update-ADTDesktop
+
+ Refreshes the Windows Explorer Shell, reloading the desktop icons and environment variables.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Update-ADTDesktop
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Refreshing the Desktop and the Windows Explorer environment process block.'
+ try
+ {
+ try
+ {
+ [PSADT.GUI.Explorer]::RefreshDesktopAndEnvironmentVariables()
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to refresh the Desktop and the Windows Explorer environment process block."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Update-ADTEnvironmentPsProvider
+#
+#-----------------------------------------------------------------------------
+
+function Update-ADTEnvironmentPsProvider
+{
+ <#
+ .SYNOPSIS
+ Updates the environment variables for the current PowerShell session with any environment variable changes that may have occurred during script execution.
+
+ .DESCRIPTION
+ Environment variable changes that take place during script execution are not visible to the current PowerShell session.
+
+ Use this function to refresh the current PowerShell session with all environment variable settings.
+
+ .PARAMETER LoadLoggedOnUserEnvironmentVariables
+ If script is running in SYSTEM context, this option allows loading environment variables from the active console user. If no console user exists but users are logged in, such as on terminal servers, then the first logged-in non-console user.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Update-ADTEnvironmentPsProvider
+
+ Refreshes the current PowerShell session with all environment variable settings.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Update-ADTEnvironmentPsProvider
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$LoadLoggedOnUserEnvironmentVariables
+ )
+
+ begin
+ {
+ # Perform initial setup.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+
+ # Determine the user SID to base things off of.
+ $userSid = if ($LoadLoggedOnUserEnvironmentVariables -and ($runAsActiveUser = & $Script:CommandTable.'Get-ADTRunAsActiveUser'))
+ {
+ $runAsActiveUser.SID
+ }
+ else
+ {
+ [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
+ }
+ }
+
+ process
+ {
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message 'Refreshing the environment variables for this PowerShell session.'
+ try
+ {
+ try
+ {
+ # Update all session environment variables. Ordering is important here: user variables comes second so that we can override system variables.
+ & $Script:CommandTable.'Get-ItemProperty' -LiteralPath 'Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment', "Microsoft.PowerShell.Core\Registry::HKEY_USERS\$userSid\Environment" | & {
+ process
+ {
+ $_.PSObject.Properties | & {
+ process
+ {
+ if ($_.Name -notmatch '^PS((Parent)?Path|ChildName|Provider)$')
+ {
+ [System.Environment]::SetEnvironmentVariable($_.Name, $_.Value)
+ }
+ }
+ }
+ }
+ }
+
+ # Set PATH environment variable separately because it is a combination of the user and machine environment variables.
+ [System.Environment]::SetEnvironmentVariable('PATH', [System.String]::Join(';', (([System.Environment]::GetEnvironmentVariable('PATH', 'Machine'), [System.Environment]::GetEnvironmentVariable('PATH', 'User')).Split(';').Trim() | & { process { if ($_) { return $_ } } } | & $Script:CommandTable.'Select-Object' -Unique)))
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_ -LogMessage "Failed to refresh the environment variables for this PowerShell session."
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Update-ADTGroupPolicy
+#
+#-----------------------------------------------------------------------------
+
+function Update-ADTGroupPolicy
+{
+ <#
+ .SYNOPSIS
+ Performs a gpupdate command to refresh Group Policies on the local machine.
+
+ .DESCRIPTION
+ This function performs a gpupdate command to refresh Group Policies on the local machine. It updates both Computer and User policies by forcing a refresh using the gpupdate.exe utility.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any objects.
+
+ .EXAMPLE
+ Update-ADTGroupPolicy
+
+ Performs a gpupdate command to refresh Group Policies on the local machine.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Update-ADTGroupPolicy
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ # Make this function continue on error.
+ & $Script:CommandTable.'Initialize-ADTFunction' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorAction SilentlyContinue
+ }
+
+ process
+ {
+ # Handle each target separately so we can report on it.
+ foreach ($target in ('Computer', 'User'))
+ {
+ try
+ {
+ try
+ {
+ # Invoke gpupdate.exe and cache the results. An exit code of 0 is considered successful.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message "$(($msg = "Updating Group Policies for the $target"))."
+ $gpUpdateResult = & "$([System.Environment]::SystemDirectory)\cmd.exe" /c "echo N | gpupdate.exe /Target:$target /Force" 2>&1
+ if (!$Global:LASTEXITCODE)
+ {
+ continue
+ }
+
+ # If we're here, we had a bad exit code.
+ & $Script:CommandTable.'Write-ADTLogEntry' -Message ($msg = "$msg failed with exit code [$Global:LASTEXITCODE].") -Severity 3
+ $naerParams = @{
+ Exception = [System.Runtime.InteropServices.ExternalException]::new($msg, $Global:LASTEXITCODE)
+ Category = [System.Management.Automation.ErrorCategory]::InvalidResult
+ ErrorId = 'GpUpdateFailure'
+ TargetObject = $gpUpdateResult
+ RecommendedAction = "Please review the result in this error's TargetObject property and try again."
+ }
+ throw (& $Script:CommandTable.'New-ADTErrorRecord' @naerParams)
+ }
+ catch
+ {
+ & $Script:CommandTable.'Write-Error' -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ & $Script:CommandTable.'Invoke-ADTFunctionErrorHandler' -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+ }
+
+ end
+ {
+ & $Script:CommandTable.'Complete-ADTFunction' -Cmdlet $PSCmdlet
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Write-ADTLogEntry
+#
+#-----------------------------------------------------------------------------
+
+function Write-ADTLogEntry
+{
+ <#
+ .SYNOPSIS
+ Write messages to a log file in CMTrace.exe compatible format or Legacy text file format.
+
+ .DESCRIPTION
+ Write messages to a log file in CMTrace.exe compatible format or Legacy text file format and optionally display in the console. This function supports different severity levels and can be used to log debug messages if required.
+
+ .PARAMETER Message
+ The message to write to the log file or output to the console.
+
+ .PARAMETER Severity
+ Defines message type. When writing to console or CMTrace.exe log format, it allows highlighting of message type.
+ Options: 0 = Success (highlighted in green), 1 = Information (default), 2 = Warning (highlighted in yellow), 3 = Error (highlighted in red)
+
+ .PARAMETER Source
+ The source of the message being logged.
+
+ .PARAMETER ScriptSection
+ The heading for the portion of the script that is being executed.
+
+ .PARAMETER LogType
+ Choose whether to write a CMTrace.exe compatible log file or a Legacy text log file.
+
+ .PARAMETER LogFileDirectory
+ Set the directory where the log file will be saved.
+
+ .PARAMETER LogFileName
+ Set the name of the log file.
+
+ .PARAMETER PassThru
+ Return the message that was passed to the function.
+
+ .PARAMETER DebugMessage
+ Specifies that the message is a debug message. Debug messages only get logged if -LogDebugMessage is set to $true.
+
+ .INPUTS
+ System.String
+
+ The message to write to the log file or output to the console.
+
+ .OUTPUTS
+ System.String[]
+
+ This function returns the provided output if -PassThru is specified.
+
+ .EXAMPLE
+ Write-ADTLogEntry -Message "Installing patch MS15-031" -Source 'Add-Patch'
+
+ Writes a log entry indicating that patch MS15-031 is being installed.
+
+ .EXAMPLE
+ Write-ADTLogEntry -Message "Script is running on Windows 11" -Source 'Test-ValidOS'
+
+ Writes a log entry indicating that the script is running on Windows 11.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com/docs/reference/functions/Write-ADTLogEntry
+ #>
+
+ [CmdletBinding()]
+ [OutputType([System.Collections.Specialized.StringCollection])]
+ param
+ (
+ [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [AllowEmptyCollection()]
+ [System.String[]]$Message,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateRange(0, 3)]
+ [System.Nullable[System.UInt32]]$Severity,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$Source,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$ScriptSection,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('CMTrace', 'Legacy')]
+ [System.String]$LogType,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LogFileDirectory,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateNotNullOrEmpty()]
+ [System.String]$LogFileName,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$PassThru,
+
+ [Parameter(Mandatory = $false)]
+ [System.Management.Automation.SwitchParameter]$DebugMessage
+ )
+
+ begin
+ {
+ # Get the caller's preference values and set them within this scope.
+ & $Script:CommandTable.'Set-ADTPreferenceVariables' -SessionState $ExecutionContext.SessionState
+
+ # Set up collector for piped in messages.
+ $messages = [System.Collections.Specialized.StringCollection]::new()
+ }
+
+ process
+ {
+ # Return early if the InformationPreference is silent.
+ if (($Severity -le 1) -and ($InformationPreference -match '^(SilentlyContinue|Ignore)$'))
+ {
+ return
+ }
+
+ # Add all non-null messages to the collector.
+ $null = $Message | & {
+ process
+ {
+ if (![System.String]::IsNullOrWhiteSpace($_))
+ {
+ $messages.Add($_)
+ }
+ }
+ }
+ }
+
+ end
+ {
+ # Return early if we have no messages to write out.
+ if (!$messages.Count)
+ {
+ return
+ }
+
+ # If we don't have an active session, write the message to the verbose stream (4).
+ if (& $Script:CommandTable.'Test-ADTSessionActive')
+ {
+ (& $Script:CommandTable.'Get-ADTSession').WriteLogEntry($messages, $Severity, $Source, $ScriptSection, $null, $DebugMessage, $LogType, $LogFileDirectory, $LogFileName)
+ }
+ elseif (!$DebugMessage)
+ {
+ if ([System.String]::IsNullOrWhiteSpace($Source))
+ {
+ $Source = [PSADT.Module.DeploymentSession]::GetLogEntryCaller([System.Management.Automation.CallStackFrame[]](& $Script:CommandTable.'Get-PSCallStack')).Command
+ }
+ $messages -replace '^', "[$([System.DateTime]::Now.ToString('O'))] [$Source] :: " | & $Script:CommandTable.'Write-Verbose'
+ }
+
+ # Return the provided message if PassThru is true.
+ if ($PassThru)
+ {
+ return $messages
+ }
+ }
+}
+
+
+#-----------------------------------------------------------------------------
+#
+# MARK: Module Constants and Function Exports
+#
+#-----------------------------------------------------------------------------
+
+# Rethrowing caught exceptions makes the error output from Import-Module look better.
+try
+{
+ # Set all functions as read-only, export all public definitions and finalise the CommandTable.
+ & $Script:CommandTable.'Set-Item' -LiteralPath $FunctionPaths -Options ReadOnly
+ & $Script:CommandTable.'Get-Item' -LiteralPath $FunctionPaths | & { process { $CommandTable.Add($_.Name, $_) } }
+ & $Script:CommandTable.'New-Variable' -Name CommandTable -Value ([System.Collections.ObjectModel.ReadOnlyDictionary[System.String, System.Management.Automation.CommandInfo]]::new($CommandTable)) -Option Constant -Force -Confirm:$false
+ if (!$MinimumStartup)
+ {
+ & $Script:CommandTable.'Export-ModuleMember' -Function $Module.Manifest.FunctionsToExport
+ }
+ else
+ {
+ & $Script:CommandTable.'Export-ModuleMember' -Function New-ADTTemplate
+ }
+
+ # Define object for holding all PSADT variables.
+ & $Script:CommandTable.'New-Variable' -Name ADT -Option Constant -Value ([pscustomobject]@{
+ Callbacks = [pscustomobject]@{
+ Starting = [System.Collections.Generic.List[System.Management.Automation.CommandInfo]]::new()
+ Opening = [System.Collections.Generic.List[System.Management.Automation.CommandInfo]]::new()
+ Closing = [System.Collections.Generic.List[System.Management.Automation.CommandInfo]]::new()
+ Finishing = [System.Collections.Generic.List[System.Management.Automation.CommandInfo]]::new()
+ }
+ Directories = [pscustomobject]@{
+ Defaults = ([ordered]@{
+ Script = $PSScriptRoot
+ Config = [System.IO.Path]::Combine($PSScriptRoot, 'Config')
+ Strings = [System.IO.Path]::Combine($PSScriptRoot, 'Strings')
+ }).AsReadOnly()
+ Script = $null
+ Config = $null
+ Strings = $null
+ }
+ Durations = [pscustomobject]@{
+ ModuleImport = $null
+ ModuleInit = $null
+ }
+ Sessions = $null
+ SessionState = $ExecutionContext.SessionState
+ TerminalServerMode = $false
+ Environment = $null
+ Language = $null
+ Config = $null
+ Strings = $null
+ LastExitCode = 0
+ Initialized = $false
+ })
+
+ # Create empty list for sessions.
+ if (!$MinimumStartup)
+ {
+ $ADT.Sessions = [System.Collections.Generic.List[PSADT.Module.DeploymentSession]]::new()
+ }
+ else
+ {
+ $ADT.Sessions = @()
+ }
+
+ # Define object for holding all dialog window variables.
+ & $Script:CommandTable.'New-Variable' -Name Dialogs -Option Constant -Value ([ordered]@{
+ Box = ([ordered]@{
+ Buttons = ([ordered]@{
+ OK = 0
+ OKCancel = 1
+ AbortRetryIgnore = 2
+ YesNoCancel = 3
+ YesNo = 4
+ RetryCancel = 5
+ CancelTryAgainContinue = 6
+ }).AsReadOnly()
+ Icons = ([ordered]@{
+ None = 0
+ Stop = 16
+ Question = 32
+ Exclamation = 48
+ Information = 64
+ }).AsReadOnly()
+ DefaultButtons = ([ordered]@{
+ First = 0
+ Second = 256
+ Third = 512
+ }).AsReadOnly()
+ }).AsReadOnly()
+ Classic = [pscustomobject]@{
+ ProgressWindow = [pscustomobject]@{
+ SyncHash = [System.Collections.Hashtable]::Synchronized(@{})
+ XamlCode = $null
+ PowerShell = $null
+ Invocation = $null
+ Running = $false
+ }
+ Assets = [pscustomobject]@{
+ Icon = $null
+ Logo = $null
+ Banner = $null
+ }
+ Font = $(if (!$MinimumStartup) { [System.Drawing.SystemFonts]::MessageBoxFont })
+ BannerHeight = 0
+ Width = 450
+ }
+ Fluent = [pscustomobject]@{
+ ProgressWindow = [pscustomobject]@{
+ Running = $false
+ }
+ }
+ }).AsReadOnly()
+
+ # Registry path transformation constants used within Convert-ADTRegistryPath.
+ & $Script:CommandTable.'New-Variable' -Name Registry -Option Constant -Value ([ordered]@{
+ PathMatches = [System.Collections.ObjectModel.ReadOnlyCollection[System.String]]$(
+ ':\\'
+ ':'
+ '\\'
+ )
+ PathReplacements = ([ordered]@{
+ '^HKLM' = 'HKEY_LOCAL_MACHINE\'
+ '^HKCR' = 'HKEY_CLASSES_ROOT\'
+ '^HKCU' = 'HKEY_CURRENT_USER\'
+ '^HKU' = 'HKEY_USERS\'
+ '^HKCC' = 'HKEY_CURRENT_CONFIG\'
+ '^HKPD' = 'HKEY_PERFORMANCE_DATA\'
+ }).AsReadOnly()
+ WOW64Replacements = ([ordered]@{
+ '^(HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\|HKEY_CURRENT_USER\\SOFTWARE\\Classes\\|HKEY_CLASSES_ROOT\\)(AppID\\|CLSID\\|DirectShow\\|Interface\\|Media Type\\|MediaFoundation\\|PROTOCOLS\\|TypeLib\\)' = '$1Wow6432Node\$2'
+ '^HKEY_LOCAL_MACHINE\\SOFTWARE\\' = 'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\'
+ '^HKEY_LOCAL_MACHINE\\SOFTWARE$' = 'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node'
+ '^HKEY_CURRENT_USER\\Software\\Microsoft\\Active Setup\\Installed Components\\' = 'HKEY_CURRENT_USER\Software\Wow6432Node\Microsoft\Active Setup\Installed Components\'
+ }).AsReadOnly()
+ }).AsReadOnly()
+
+ # Lookup table for preference variables and their associated CommonParameter name.
+ & $Script:CommandTable.'New-Variable' -Name PreferenceVariableTable -Option Constant -Value ([ordered]@{
+ 'InformationAction' = 'InformationPreference'
+ 'ProgressAction' = 'ProgressPreference'
+ 'WarningAction' = 'WarningPreference'
+ 'Confirm' = 'ConfirmPreference'
+ 'Verbose' = 'VerbosePreference'
+ 'WhatIf' = 'WhatIfPreference'
+ 'Debug' = 'DebugPreference'
+ }).AsReadOnly()
+
+ # Send the module's database into the C# code for internal access.
+ if (!$MinimumStartup)
+ {
+ [PSADT.Module.InternalDatabase]::Init($ADT)
+ }
+}
+catch
+{
+ throw
+}
+
+# Import the XML code for the classic progress window.
+$Dialogs.Classic.ProgressWindow.XamlCode = [System.IO.StringReader]::new(@'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+'@)
+
+# Determine how long the import took.
+$ADT.Durations.ModuleImport = [System.DateTime]::Now - $ModuleImportStart
+& $Script:CommandTable.'Remove-Variable' -Name ModuleImportStart -Force -Confirm:$false
+
+
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA+6quY4pLht5Bk
+# HSuKVseFCR4bwfjuKoos2vtkWr2zyaCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgYQDU9UnOYjMif1i0fXrhRrkRQtUmrHTx3q4mAWI0Ik8w
+# DQYJKoZIhvcNAQEBBQAEggGABow9SjBNcP2of7hqFbHtGDeJ11teFhWqyc8cmWw7
+# MVwR06U2AbVYunv9cRpE9oYZ64lMLltoMJ3l+L/Q34sjRQ/CpeQGWPWbhO4pU/T9
+# TwPFgaueLYiEmCXKjSVVAhQVuxn7aMmNFAO95G03EJ4ayQ10U8SH81oNpLiNRppn
+# AFHwhIOWOei6OM0E+uQ9RSHAZvH3CEhvycuKSW6NqPVI//2eGrvYWX9mvV8mMXwx
+# p51gBCmXsGsmbzbg5F9d0LtjeSSANv62nvcJZkmFBlnJqPQH9IiWx7tS/cxR7BtF
+# +siJ1dPHfwYWN0CMD2MkgN+GhdTZvUbuO2cD3RYr1b1QQq4xTFnwZdWc6Z2dggDP
+# G+jOoOXdt8rMAU7xfi7rFGboBmxDXHouIZusQUxjlrN+ly4/+h51C08oWT1Sq8Zg
+# /tmK9oept6wd6gKAMtix6R1AYpJHep56/2VpinO2xJ+qQxGzh2KgoRSFLrZNkqIZ
+# mnSrGTArDVVNesmxgiCfY5RBoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEICMfO9VM
+# i3ZA+nG6OAC3uM00DToklU1ldBuctedBZUS/AhEAphU24g6jP7INqQEr4kOXvRgP
+# MjAyNTAyMjMyMzQ4NDBaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ4NDBaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCBilQIzWUtFMa0goT6GLhSHgbj5UzhQFkMvs59X
+# wGuDjDA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgAvJNpe0Wpwn42lQsa6682c
+# oVhTvu9BJFygXVI4+6/XsCAWrbKqZomp4mFa8dDXdQWUkO6Uqx2L+xQEkyc3Y5XE
+# v+rXKbKeUHpy3Zi0Ox7Ocs/N0G7zGX8lJe3UnVKWJ5cdmOXvtbDR4ukfSVkJbj7+
+# l0VaR2jth9EdoW8/rK8Te+Z7Bv2VoHMCfTno2FMC3ZJLRDWLQUkQqPv5JIjbldSp
+# SCjYY4Lsm8F84Jo+w757hOcS71+DRtCEHwcHDl2fqvAibHz+RyKWnM5YrgEi21II
+# UQf8vXeskaP8y9yuU5j+9IwzmignDdTK++xRd1aLH1bB6aOhlBtBtT5K4KQtc1L7
+# mWJW8PCoiw3C7jDcLQCm5vrRMkV4j4EfqeKeOaORNIZCRhrMLd8EbO9f9Nlj/fEI
+# 6mBKKdywezGffh8VR/yPsd7qQlkQsLxbBICY+qeh29U73nSYeeDPjc+10loXGWN5
+# ZCelsNyynCuggD2OUWJ1dkx3k9uWNil2kvEtSOWiruHrylj2AHXt8coAynNG/QTq
+# gkagmMhxQuJK+MAe9CQ0Ap7l4L9jNbW9L9CEWUvnwdY0YtZeYmX85GAH5xxylAsY
+# 66SR285l4LH7Q5hgEA3WW+k//J0ismZSAUC8oq9EgOfbc+nF7L/IcmCbOCyrnifw
+# rkwoqFUN+NLpdUQfkO4S+g==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/ar/strings.psd1 b/PSAppDeplyToolkit/Strings/ar/strings.psd1
new file mode 100644
index 0000000..07ce489
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/ar/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "تم."
+ Error = "فشل."
+ FastRetry = "غير مكتمل."
+ RestartRequired = "تم. يجب إعادة تشغيل النظام."
+ Start = "بدأ."
+ }
+ BlockExecution = @{
+ Message = "تم تعطيل تشغيل هذا التطبيق مؤقتًا بحيث يمكن إكمال عملية التثبيت."
+ }
+ ClosePrompt = @{
+ ButtonClose = "إغلاق البرامج"
+ ButtonContinue = "متابعة"
+ ButtonContinueTooltip = "قم باختيار `"متابعة`" فقط بعد إغلاق التطبيق/التطبيقات المدرجة أعلاه."
+ ButtonDefer = "تأجيل"
+ CountdownMessage = "ملاحظة: سيتم إغلاق البرنامج/البرامج بشكل تلقائي خلال:"
+ Message = "يجب إغلاق البرامج التالية قبل التمكن من متابعة عملية التثبيت.`n`nيرجى حفظ عملك، وإغلاق البرامج، ومن ثم المتابعة. يمكنك بدلا من ذلك، حفظ عملك والنقر فوق `"إغلاق البرامج`"."
+ }
+ DeferPrompt = @{
+ Deadline = "الموعد النهائي:"
+ ExpiryMessage = "بإمكانك اختيار تأجيل التثبيت إلى حين انتهاء صلاحية التأجيل:"
+ RemainingDeferrals = "التأجيلات المتبقية:"
+ WarningMessage = "بمجرد انتهاء صلاحية التأجيل، لن يكون لديك خيار التأجيل بعد الآن."
+ WelcomeMessage = "التطبيق التالي على وشك التثبيت:"
+ }
+ DeploymentType = @{
+ Install = "تثبيت"
+ Repair = "إصلاح"
+ Uninstall = "إزالة التثبيت"
+ }
+ DiskSpace = @{
+ Message = "ليس لديك مساحة كافية في القرص لإتمام عملية تثبيت:`n{0}`n`nالمساحة اللازمة: {1} م ب`nالمساحة المتاحة: {2} م ب`n`nيرجى تحرير مساحة قرص كافية كي تتم متابعة عملية التثبيت."
+ }
+ Progress = @{
+ MessageInstall = "جاري التثبيت. يرجى الانتظار..."
+ MessageInstallDetail = "سيتم إغلاق هذه النافذة تلقائياً عند اكتمال التثبيت."
+ MessageRepair = "جارٍ إصلاح. يرجى الانتظار..."
+ MessageRepairDetail = "سيتم إغلاق هذه النافذة تلقائياً عند اكتمال الإصلاح."
+ MessageUninstall = "جارٍ إزالة التثبيت. يرجى الانتظار..."
+ MessageUninstallDetail = "سيتم إغلاق هذه النافذة تلقائياً عند اكتمال إلغاء التثبيت."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "تقليل"
+ ButtonRestartNow = "إعادة التشغيل الآن"
+ Message = "كي تكتمل عملية التثبيت، يجب عليك إعادة تشغيل حاسوبك."
+ MessageRestart = "ستتم إعادة تشغيل حاسوبك بشكل تلقائي عند نهاية العد التنازلي."
+ MessageTime = "يرجى حفظ عملك وإعادة التشغيل خلال الوقت المخصص."
+ TimeRemaining = "الزمن المتبقي:"
+ Title = "مطلوب إعادة التشغيل"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "سيستمر {0} تلقائيا في:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployDeployToolkit - التطبيق {0}'
+ DialogMessage = 'يرجى حفظ عملك قبل المتابعة حيث سيتم إغلاق التطبيقات التالية تلقائيًا.'
+ DialogMessageNoProcesses = 'الرجاء تحديد تثبيت لمتابعة التثبيت. إذا كان لديك أي تأجيلات متبقية، يمكنك أيضاً اختيار تأخير التثبيت.'
+ ButtonDeferRemaining = 'تبقى'
+ ButtonLeftText = 'التأجيل'
+ ButtonRightText = 'إغلاق التطبيقات وتثبيتها'
+ ButtonRightTextNoProcesses = 'التثبيت'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDRJQ2YvnooEvBu
+# 6omMMFVTNnathijEsfHxOLfuOuhCf6CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgwa4fYcXzKWV56XInno7ktDRR7tumSZjs7OhEJ0UywI0w
+# DQYJKoZIhvcNAQEBBQAEggGAsyEAgIz73TsvR7cGOblNwvlLnfC4oPI/DhgpLYSu
+# bndl03eAKW+d9SCN0QpYdSE81wTcz/yWD6UusLUOeZqCjLu2H9A4lpaFB82n82h3
+# dNPri9YavXW8LgQOrMXpGMMz06BplD6hlu/yi4TstkSmVf8pe+mk8DDPBHkcDJLq
+# QaNkTH04lZJ7srTERqeZ3aU8a6gyUesqMnMuWkjYrjXCdQKryvt4io7S5iL1osnY
+# pQnNyQgHvv56BtNz5Lm0srH34Q9/XIiizrhdNxEGX4aA5PZXBhWTFdT/0FnXBTZg
+# 7SKGeEPGsnuBUlqwKjpm3/6ltpchRTbrKdPqoLGGo3N1Aqby+tQI/9zbIfYOAvvb
+# ZMKIN16Svdniii9S5vJfJDvvmfLJK0JM1hGCdoUdNanmKcjC7hh5ZhbjggkhGP9z
+# swYd1VE99FcAMyFF3rEAMUjF+cGvtBA9BK13ttHkhQRTjaVOHXuAEwtgX1U+o4ei
+# aLzUDaTDjKAZHeHe/Qf1LxWSoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIAFyKUEF
+# Mi3Vk4+wjxtQpWixJc+1mszhf0j+QLE2mKm8AhEAmgQJHn2zZ+MHX8wTvK6DiRgP
+# MjAyNTAyMjMyMzQ3MjZaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3MjZaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCDr4nLKvCioQwiC28+9mSQKwhxLK+U4m/kw5oyq
+# ueotADA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgCpXDsFuwhMQDATW750q8kP
+# BVqdXrxiQYZI5EV7bREcooEmHhIllyOPBnsSauiZlWUAOtdCFXcyblx1Rkx9aUSI
+# GHHZiwda3WJPJaiSrj8D/mUB17WtDISig/0PY9VPAWJv2Sx7FSCvcvaH+qSp+TcK
+# yevq5qzyVNcEDLq7PSOCe7rhG7MxEuNpuSUsv6Z82aZxwnvtzt4Q2nZzsrnOB7jM
+# UsiqMgebOKZ+gixCoynxfmt44AK6eRgeuncafFz+tTxxdSZhI28qTUkvgXFf6xvg
+# cmPgc4qHoczVMZJS6L6fGaGKSXniXWS1hZfqmJUZP+1W7CS2inyJGBNpTgHXdZvW
+# C4NAZjJOrORWqMcN/mdS8g3VBGRVxr9ep00xAir4BFnP0YylCPSGDnmTLcAZp5+r
+# +xq1+sKIlkqUNkQGiAP0ypX8EnJinaz+egFVkXhhKORkaBTMH62fHE7EA+n5nH5O
+# M/+VO+0t1aMGm0qvf/wBGdUVNw53oZ/+uVsl4pVOd90v/JDYvbaJssVSIimzm1pe
+# +56NOhq99gY/qlIVJTC3ZQ6z12O30SJj5JH1su9Qyra6DqESQE/I4sTzv63TQXhM
+# tIcqPS94/sGrX/GQ3TCgf5xWdGVj9TzRpBO5cNvR6dcjsoQjTpp5ELs6NhIZ4ZhC
+# YZ6YzAUtqQnyq700rWXVag==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/cz/strings.psd1 b/PSAppDeplyToolkit/Strings/cz/strings.psd1
new file mode 100644
index 0000000..3edae78
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/cz/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "dokončena."
+ Error = "se nepodařila."
+ FastRetry = "nedokončena."
+ RestartRequired = "dokončena. Je nutné restartovat počítač."
+ Start = "zahájena."
+ }
+ BlockExecution = @{
+ Message = "Spuštění této aplikace bylo dočasně zakázáno, aby mohla proběhnout instalace."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Ukončit programy"
+ ButtonContinue = "Pokračovat"
+ ButtonContinueTooltip = "Klikněte na `"Pokračovat`", až budete mít výše uvedené aplikace zavřené."
+ ButtonDefer = "Odložit"
+ CountdownMessage = "Upozornění: Programy budou automaticky zavřené za:"
+ Message = "Následující programy musí být zavřené, aby instalace mohla pokračovat. Prosím, uložte svou práci, zavřete program a potom klikněte na `"Pokračovat`". Případně můžete svou práci uložit a kliknout na tlačítko `"Ukončit programy`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Termín:"
+ ExpiryMessage = "Instalaci můžete několikrát odložit:"
+ RemainingDeferrals = "Zbývající počet odložení:"
+ WarningMessage = "Jakmile vyčerpáte všechna odložení, už nebudete mít šanci odložit instalaci."
+ WelcomeMessage = "Nasledující aplikace bude nainstalována:"
+ }
+ DeploymentType = @{
+ Install = "Instalace"
+ Repair = "Oprava"
+ Uninstall = "Odinstalace"
+ }
+ DiskSpace = @{
+ Message = "Nemáte dostatek volného místa na instalaci aplikace:`n{0}`n`nPotřebné místo na disku: {1}MB`nDostupné místo na disku: {2}MB`n`nUvolněte prosím dostatek místa k pokračovaní instalace."
+ }
+ Progress = @{
+ MessageInstall = "Instalace právě probíhá. Prosím čekejte..."
+ MessageInstallDetail = "Toto okno se po dokončení instalace automaticky zavře."
+ MessageRepair = "Oprava právě probíhá. Prosím čekejte..."
+ MessageRepairDetail = "Toto okno se po dokončení opravy automaticky zavře."
+ MessageUninstall = "Probíhá odinstalace. Prosím čekejte..."
+ MessageUninstallDetail = "Po dokončení odinstalace se toto okno automaticky zavře."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimalizovat"
+ ButtonRestartNow = "Restartovat nyní"
+ Message = "Pro dokončení instalace musíte váš počítač restartovat."
+ MessageRestart = "Na konci odpočítávání, bude váš počítač automaticky restartovaný."
+ MessageTime = "Prosím, uložte si práci a restartujte počítač ve stanoveném čase."
+ TimeRemaining = "Zbývající čas:"
+ Title = "Je nutné restartovat počítač."
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} bude automaticky pokračovat za:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Aplikace {0}'
+ DialogMessage = 'Před pokračováním v práci ji uložte, protože následující aplikace budou automaticky uzavřeny.'
+ DialogMessageNoProcesses = 'Chcete-li pokračovat v instalaci, vyberte možnost Instalovat. Pokud vám zbývají nějaké odklady, můžete také zvolit odložení instalace.'
+ ButtonDeferRemaining = 'zůstat'
+ ButtonLeftText = 'Odložení'
+ ButtonRightText = 'Zavření aplikací a instalace'
+ ButtonRightTextNoProcesses = 'Instalace'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC94afUJ3Nvd65t
+# OaYjunms68dpv6KLTIfGLd1bXl3vvaCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgQUj8e94a/IxuG2CicbaTm4B55Mu2fojZIZUxovkIaGcw
+# DQYJKoZIhvcNAQEBBQAEggGAfZAkXXfJUeyv8S3mAUecCeU12G/xt/L9ZDjG+lL5
+# tdmfpu6e9Wd3ylnu5gFFhnkbG+9gsmWJimEcDhlPjFpNYwKtn/9sYHuDtNqeIqo1
+# C2kqsVqDCnpShlsUWWcQ2uaRleqAq9QFwbhOV4Do8jxA3VDul6rrugjK/d7PuFt7
+# bziBHsO3Dyu5ioeA1YLAKkcS0VuUdCYZvCVo/PrQjroR8CQ52qlfVb53DnFxMnWJ
+# /xYV+13NIlzizc8Qr36Zv7fgMjXnfYAHuBRRea+A1y2UN8ELqSucJiFziP+W4YNp
+# 3nSuGY8qrXGUFlHY52+3k19T4WntYjoB+AvqH4nBAi+DA/wqBV49oqK8noTFzZXq
+# aw2/6/fQ8JDUdVsvLwLz2vN3iCejHYtvLTkronPuuRB72TrVPnkwUtXaXiF16MS2
+# blqMRfZONYnZZitrji8tatxirustkqbglwpFG1oyBZK/u+FGZXVqr/Ww/0htqFEt
+# ZirSPmcQ9nLEiwJl+k5MB8nCoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIApgy8F0
+# GqxNVjNuGpSsHDCAqYYDag1wCpUNmDmAKQziAhEAqgGYLGGzSLUXKSh7yY0zbBgP
+# MjAyNTAyMjMyMzQ3MjlaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3MjlaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCCT7+mpWgm1rgVXC4kNzI6MuWFRY3xXBXHX5jDv
+# PKu7HzA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgAm8LZ22rmvgWFASrqCt5OY
+# Vod/USc2k3+4LUPNH5dwb7xE7bMet+tnpSTZ088NB4r7UqffAPVbTtCTYu+bXICA
+# iD/+h0NvmrmjmzjpoLCkg85sFTG/wSxlRBAC1tnvFCulb3QpMTMeth/qnhVQxWjo
+# DBz4nbQG+rg0uz9CHKLnTrMzFM9Q+/J9SV4bB8sLBUt5UczJ+aJzKUh4FkzOFSdr
+# LN0ltSsDfL0ju/yH2jJ/cGN+X1bAo8l+veNXvdeNce2FIDWHuKeiifm5H3lIoDik
+# aCUujng4j75+NboHopBagLeydKnPF5xScGpK35UroOIT8zegDI7zVvNbCZ0tImzS
+# Qqy/BCpuCnXMbDC6qJVr3gr4pNb3TV+M9CO9CL7YvgxezAG68uw/H0uKA+JLEY+7
+# o+WBBPmx5yiim/qeO77fAdvNkt2glf+sWS89xqAl+zu5U6zyWg9vdLMCGShe2FFI
+# 8RoH7uXmXYUTkXkLHthnkrWODV20zNcdi4uDu33mGqmp56io/aiGM8Ak1/wJuAFH
+# kp2AJiehvfa6c+85ABt+QJy58n5MGADcKC65tYAnOe2pG/sNgXdD/TqkZvVqaQZT
+# FF1C3o62rEG2rXpjV78qLfl+Le+CPLzjwSs2U8xgUIaz1SnkxcE4xSnEAcSepGiD
+# 2UmMBLqakZXESBcrmWCzIQ==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/da/strings.psd1 b/PSAppDeplyToolkit/Strings/da/strings.psd1
new file mode 100644
index 0000000..f4f424b
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/da/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "færdig."
+ Error = "fejlet."
+ FastRetry = "ikke færdig."
+ RestartRequired = "færdig. En genstart er nødvendig."
+ Start = "startet."
+ }
+ BlockExecution = @{
+ Message = "Opstart af denne applikation er midlertidigt blokeret da en installationsproces er under afvikling."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Luk Programmer"
+ ButtonContinue = "Fortsæt"
+ ButtonContinueTooltip = "Vælg kun `"Fortsæt`" efter at du har afsluttet de ovenfor nævnte programmer."
+ ButtonDefer = "Udsæt"
+ CountdownMessage = "BEMÆRK: Programmet/Programmerne vil automatisk blive lukket om:"
+ Message = "Følgende programmer skal lukkes før installationen kan fortsætte.`n`nGem dit arbejde, luk programmerne og fortsæt. Alternativt kan du gemme dit arbejde og trykke på `"Luk Programmer`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Deadline:"
+ ExpiryMessage = "Du kan vælge at udsætte installationen indtil udsættelsesperioden udløber:"
+ RemainingDeferrals = "Udsættelser tilbage:"
+ WarningMessage = "Når udsættelsesperioden udløber kan du ikke længere udsætte installationen."
+ WelcomeMessage = "Følgende applikation vil nu blive installeret:"
+ }
+ DeploymentType = @{
+ Install = "Installation"
+ Repair = "Reparere"
+ Uninstall = "Afinstallation"
+ }
+ DiskSpace = @{
+ Message = "Du har ikke plads nok til at færdiggøre installationen af:`n{0}`n`nPlads krævet: {1}MB`nPlads tilgængelig: {2}MB`n`nVær venlig at frigøre nok diskplads før du fortsætter installationen."
+ }
+ Progress = @{
+ MessageInstall = "Installation i gang. Vent venligst..."
+ MessageInstallDetail = "Dette vindue lukker automatisk, når installationen er færdig."
+ MessageRepair = "Reparere i gang. Vent venligst..."
+ MessageRepairDetail = "Dette vindue lukkes automatisk, når reparationen er færdig."
+ MessageUninstall = "Afinstallation i gang. Vent venligst..."
+ MessageUninstallDetail = "Dette vindue lukkes automatisk, når afinstallationen er færdig."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimere"
+ ButtonRestartNow = "Genstart Nu"
+ Message = "For at færdiggøre installationen skal du genstarte din computer."
+ MessageRestart = "Din computer vil automatisk blive genstartet når nedtællingen er færdig."
+ MessageTime = "Du bør venligst gemme dit arbejde og genstarte indenfor det givne tidsrum."
+ TimeRemaining = "Tid tilbage:"
+ Title = "Genstart Nødvendig"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} vil automatisk fortsætte i:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - App {0}'
+ DialogMessage = 'Gem venligst dit arbejde, før du fortsætter, da de følgende applikationer lukkes automatisk.'
+ DialogMessageNoProcesses = 'Vælg Installer for at fortsætte med installationen. Hvis du har udsættelser tilbage, kan du også vælge at udskyde installationen.'
+ ButtonDeferRemaining = 'forblive'
+ ButtonLeftText = 'Udskyde'
+ ButtonRightText = 'Luk apps og installer'
+ ButtonRightTextNoProcesses = 'Installer'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBNen3FvB102KXp
+# 15OWVyoDbOCI5e/cXStB0NUUMs3kpKCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQg6v0XPZu9wOhu2kJQLS3qj48e+6OpoCwTIN1NMLSfSN0w
+# DQYJKoZIhvcNAQEBBQAEggGAkxnnBUzsi6bUhL+Mu+MrB2rXHvvvTnvgTim8bpgT
+# fpuMT7cnlyw+TeTIHi9YCm7QoIEaOjkcZYcY/GvVLAj46hou7Spm3nyNOyg5GdUv
+# nKWfjRaRgLQBUcL9NlM0lnsx+OF/iM/j/BgtwP25yLHyS0lMESzQfnqVlJRTZh2y
+# N7qVqURqhylz8F2HLdcIT/R2FEV42d9XZCtEyx0d5tcKNJXnppaxKX2b6XN/yZjz
+# 8W23RXFjRC07uL+DLTxpbKoP50935EpbfPtUKjVgG96fIcBc1bI5jDfgnI3O/Zs7
+# EgsaRHd3ARLU9UpBOSBNVOOnD5QMzLqqbNF07v/3OViakg7NtYlBpUv1bOGXIZdU
+# DeojNTBH97LizF3mmL6kXhnvgKy/5AClbjhQz7ndNnScaVxq7bhU+t/CpYfQvicZ
+# o9g/gike5gtB0DwFsiVEO3Q4If9gC1q8fipbONkypA9BXLqSRoatvk3wIfDk72Y6
+# gzYTsQFDSzAIjZDfF37mjQVVoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIAxJMmgU
+# rJsZNT/C3isn7crat99EuJbwiCNKFVJ2odE8AhAIgsLh19j/w10zo54Qy6m3GA8y
+# MDI1MDIyMzIzNDczMlqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDczMlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEINgR0Uhdq7cLqj5q4RH40v0fD2hPtT7P7Ml18fUS
+# 41tDMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICALHsZXCpNNwTNimYyjErMjS4
+# Wf8GmxPQmLMA4LN15/iMXzns5ciwLXDAmhU/c6PXLmHckpm4KfKUBMscf8/2vkt4
+# hsHwuFl0a180vP8Po0xx1+tE00KGGPBaTDTmbPJyHv+lSx4skWqnp8WFtyFeNI1m
+# UewcJnTLvXzk4eWl7qQX0VwA/CTMTfmN0ln8mlJt5xP5cfZTRnc6wxBIdHmi4U5O
+# Sb6DpJtngZ6gjWotSYRR4llB/BlLZeSzLdxqcSynyFftuIsvSJkaJA7gJGVVcXZF
+# OA5s5CJ38svZJj8+wrwpQg64WH/pCRWNwpuTyUC0XBBl31OFWKJlVpD4+uAJVh7S
+# vGcGWGLuxSSomL6t/EwFae9T2i2jOhYzqnphV+SJGcLywRGRxqjUcDVKo6+97mZG
+# IawiyjDuz9aizGawkCdhi8fCEzY0LEPiZumQKA8PEl9U9Fq6oNm7FOixKyhGz04B
+# j+aZKFQSrFAnE+AqjBnQdqJhvCFR6ppZeS8psFXL4daracTHwcM5NmX1qw4uLV22
+# ngrkOpFQ23ed7uaBWPvq5TpIEivavh8t6fFnHHl1JmvdNs6jelMynJRTyBRp6tyl
+# YT1f5RVD5oA3/Tw+vtKOo/BWQOLJ4jKr+a6ZihF7yaQQthpIOdkWiolfFoGkxh4N
+# W/rxcX/5CzOMLY3Iclwr
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/de/strings.psd1 b/PSAppDeplyToolkit/Strings/de/strings.psd1
new file mode 100644
index 0000000..85cdbfe
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/de/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "abgeschlossen."
+ Error = "ist fehlgeschlagen."
+ FastRetry = "nicht abgeschlossen werden."
+ RestartRequired = "abgeschlossen. Ein Neustart ist erforderlich."
+ Start = "gestartet."
+ }
+ BlockExecution = @{
+ Message = "Das Starten dieser Anwendung(en) wurde vorübergehend blockiert, damit der Installationsvorgang erfolgreich durchgeführt werden kann."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Programme &schließen"
+ ButtonContinue = "&Weiter"
+ ButtonContinueTooltip = "Klicken Sie erst auf `"Weiter`", nachdem Sie die obigen Anwendung(en) geschlossen haben."
+ ButtonDefer = "&Aufschieben"
+ CountdownMessage = "HINWEIS: Diese Programme werden automatisch geschlossen:"
+ Message = "Die folgenden Programme müssen geschlossen werden, bevor die Installation fortgesetzt werden kann.`n`nBitte speichern Sie Ihre Arbeit, schließen Sie die Programme und fahren Sie dann fort. Alternativ können Sie Ihre Arbeit speichern und dann auf `"Programme Schließen`" klicken."
+ }
+ DeferPrompt = @{
+ Deadline = "Termin:"
+ ExpiryMessage = "Sie können die Installation verzögern, bis die Rückstellung abläuft:"
+ RemainingDeferrals = "Verbleibende Rückstellungen:"
+ WarningMessage = "Sobald die Rückstellung abgelaufen ist, werden Sie keine Möglichkeit mehr haben die Installation zu verschieben."
+ WelcomeMessage = "Die folgende Anwendung soll installiert werden:"
+ }
+ DeploymentType = @{
+ Install = "Installation"
+ Repair = "Reparatur"
+ Uninstall = "Deinstallation"
+ }
+ DiskSpace = @{
+ Message = "Sie haben nicht genug freien Speicherplatz um die Installation abzuschließen: {0}`n`nPlatzbedarf: {1}MB`nFreier Speicherplatz: {2}MB`n`nBitte geben Sie ausreichend Speicherplatz frei, um mit der Installation fortzufahren."
+ }
+ Progress = @{
+ MessageInstall = "Installation wird durchgeführt. Bitte warten..."
+ MessageInstallDetail = "Dieses Fenster wird automatisch geschlossen, wenn die Installation abgeschlossen ist."
+ MessageRepair = "Reparatur wird durchgeführt. Bitte warten..."
+ MessageRepairDetail = "Dieses Fenster wird automatisch geschlossen, wenn die Reparatur abgeschlossen ist."
+ MessageUninstall = "Deinstallation wird durchgeführt. Bitte warten..."
+ MessageUninstallDetail = "Dieses Fenster wird automatisch geschlossen, wenn die Deinstallation abgeschlossen ist."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimieren"
+ ButtonRestartNow = "Jetzt Neustarten"
+ Message = "Zum Abschluss der Installation müssen Sie Ihren Computer neu starten."
+ MessageRestart = "Am Ende des Countdowns wird Ihr Computer automatisch neu gestartet."
+ MessageTime = "Bitte speichern Sie Ihre Arbeit und starten Sie den Computer innerhalb der vorgegebenen Zeit neu."
+ TimeRemaining = "Verbleibende Zeit:"
+ Title = "Neustart Erforderlich"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "Die {0} wird automatisch fortgesetzt in:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Anwendung {0}'
+ DialogMessage = 'Bitte speichern Sie Ihre Arbeit, bevor Sie fortfahren, da die folgenden Anwendungen automatisch geschlossen werden.'
+ DialogMessageNoProcesses = 'Bitte wählen Sie Installieren, um mit der Installation fortzufahren. Wenn Sie noch Aufschübe haben, können Sie die Installation auch aufschieben.'
+ ButtonDeferRemaining = 'bleiben'
+ ButtonLeftText = 'Aufschieben'
+ ButtonRightText = 'Apps schließen & installieren'
+ ButtonRightTextNoProcesses = 'Installieren Sie'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDrHzwN+5McY4CY
+# +7vBn5hfNVR0y6yNNo0W4GW8SUyE26CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgcDb8meb3PqxdOMJcrUPVlkQ3H/iIYFeSgT265UVO/94w
+# DQYJKoZIhvcNAQEBBQAEggGAYuVBWWxRDAwfX8/9TvwLFgvl7and3CXm8jVjNqiH
+# Iagv9Pp03TX/NdzAX5MEhMBcZ6B8m3ndA9fdI+9t1kzIL/f99a0b8J2FvGcypyAA
+# J4yM3SdxB+FGtS5TRX/DTq66XyC7obaUHhb/b7Xfc0reG/4aJwnRXRaoWsFpHtFt
+# 2f8Za5p4JyRT2/yn6TNSI7RpZqVRLy2kOVHENTY1965s1jMc7zFvcJRYNcVIsC1r
+# i0qnuO+zB3QYTtbaFHMG2mK7ktqOXWXG02pW1LlJq/FUdL6O+s6S7ydpbvShV1RN
+# AgOOiqnXI4dgT/wziG/z/7xPcZCqQi8e8eJT2NS729Zm3uwOsPt1LKFD0NNemHw3
+# HN7lzHE6Tya7PncZYr0cfezUdq/6/Afwv/mLXT/69ftJ8DaGP4JozXCWxjT+dFBV
+# 3KreSv95amD2Q3scBjf2b2C3ltswZ86q2u//te9CXZJUhQSL75+Zr089nUUuQw/U
+# Bm50//FwzMf6KpGrlp3cpJk3oYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIEv4NCF2
+# WXoYkqKNa15UI8WmuhU0TJrMgvU+xFSPOgy6AhEA5sko1b2qLvdOh7/7er4OARgP
+# MjAyNTAyMjMyMzQ3MzRaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3MzRaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCBsBBXWjPBLqooG/4fIF4r2Dj+PzANJxd1rCQ3U
+# ncQv/zA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgBUT4YVsCP2vDJmVPUM5o8N
+# gnqhUBagXOcTdcSm2uLX1j8vn3mB++9YLKYpSrrjq0OEkTzXkVidmywhSEMvDlKy
+# urASyVqgRODMjN5eQ5rPPOxCdxYDcSSUNLmQmtqu8+MhxijAG20SMOOs3ufzBYpj
+# Rhhm3dff1EE+hVEEpTiY1lqMiCLhTuRpyN4j+t19HyB9YqoUU0pU4eRBhugT08xi
+# Aw0rtYQHHTGc8WAz1OWsbf9fTrFRRSyOAqTl6DoNlUM5gudi1fLPu5MLDrA6wm+g
+# me5dp7YNsSuZuAKFXZVN1PyPFdlnJCHdUJdRg40UMVfQMFZcsHmyCP1R9CwdBwCq
+# EUl++m1G26sPMnxO96TU70wnxgR43hdJc9REgExYuC0t7yffFYXyKCDSuPkfvBGK
+# sur624g6JZ6VsOzCQWWA8GZCVqCQM22yqgvY2h1fJjyZKzn90nciSANQaorSOoXF
+# gvFbWxiIaQoDiIFUTD4xxN+aX1Oxh/l2KqzxLd5D53aU1Lzlu9vAlGEuGIkNKEsF
+# 32LYH6VKhCnZyxryUsdYSyOYX9oA4Ai65mqLWdIvYbtZwfFDucBbxROg8uRUmGY7
+# FksPYVNYfQCfJM1clQGBK8pR9h630wTmM/s4JQkVAuIveNr+7THsGW9CMkfTnhq+
+# k/uNKooUDRfZO0UyODgZpg==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/el/strings.psd1 b/PSAppDeplyToolkit/Strings/el/strings.psd1
new file mode 100644
index 0000000..706a418
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/el/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "ολοκληρώθηκε με επιτυχία."
+ Error = "απέτυχε."
+ FastRetry = "δεν ολοκληρώθηκε."
+ RestartRequired = "ολοκληρώθηκε με επιτυχία. Απαιτείται επανεκκίνηση."
+ Start = "ξεκίνησε."
+ }
+ BlockExecution = @{
+ Message = "Η εκκίνηση αυτής της εφαρμογής έχει αποκλειστεί προσωρινά, ώστε να ολοκληρωθεί η διαδικασία εγκατάστασης."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Κλείσιμο Προγραμμάτων"
+ ButtonContinue = "Συνέχεια"
+ ButtonContinueTooltip = "Επιλέξτε `"Συνέχεια`" μόνο αφού κλείσετε τις παραπάνω εφαρμογές."
+ ButtonDefer = "Αναβολή"
+ CountdownMessage = "ΣΗΜΕΙΩΣΗ: Τα προγράμματα θα κλείσουν αυτόματα σε:"
+ Message = "Τα παρακάτω προγράμματα πρέπει να κλείσουν πριν προχωρήσει η εγκατάσταση.`n`nΠαρακαλούμε αποθηκεύστε την εργασία σας, κλείστε τα προγράμματα και επιλέξτε `"Συνέχεια`". Εναλλακτικά, αποθηκεύστε την εργασία σας και επιλέξτε `"Κλείσιμο Προγραμμάτων`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Προθεσμία:"
+ ExpiryMessage = "Μπορείτε να επιλέξετε να αναβάλλετε την εγκατάσταση μέχρι να λήξει η αναβολή:"
+ RemainingDeferrals = "Εναπομένουσες Αναβολές:"
+ WarningMessage = "Μετά τη λήξη της αναβολής, δεν θα έχετε πλέον την επιλογή να αναβάλετε."
+ WelcomeMessage = "Η παρακάτω εφαρμογή θα εγκατασταθεί:"
+ }
+ DeploymentType = @{
+ Install = "Η εγκατάσταση"
+ Repair = "Η επιδιόρθωση"
+ Uninstall = "Η απεγκατάσταση"
+ }
+ DiskSpace = @{
+ Message = "Δεν υπάρχει επαρκής χώρος στο δίσκο για να ολοκληρωθεί η εγκατάσταση του:`n{0}`n`nΑπαιτούμενος χώρος: {1}MB`nΔιαθέσιμος χώρος: {2}MB`n`nΠαρακαλώ απελευθερώστε επαρκή χώρο για να προχωρήσει η εγκατάσταση."
+ }
+ Progress = @{
+ MessageInstall = "Εγκατάσταση σε εξέλιξη. Παρακαλούμε περιμένετε..."
+ MessageInstallDetail = "Αυτό το παράθυρο θα κλείσει αυτόματα όταν ολοκληρωθεί η εγκατάσταση."
+ MessageRepair = "Επιδιόρθωση σε εξέλιξη. Παρακαλούμε περιμένετε..."
+ MessageRepairDetail = "Αυτό το παράθυρο θα κλείσει αυτόματα όταν ολοκληρωθεί η επισκευή."
+ MessageUninstall = "Απεγκατάσταση σε εξέλιξη. Παρακαλούμε περιμένετε..."
+ MessageUninstallDetail = "Αυτό το παράθυρο θα κλείσει αυτόματα όταν ολοκληρωθεί η απεγκατάσταση."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Ελαχιστοποίηση"
+ ButtonRestartNow = "Επανεκκίνηση τώρα"
+ Message = "Για να ολοκληρωθεί η εγκατάσταση, πρέπει να επανεκκινήσετε τον υπολογιστή σας."
+ MessageRestart = "Ο υπολογιστής σας θα επανεκκινηθεί αυτόματα στο τέλος της αντίστροφης μέτρησης."
+ MessageTime = "Παρακαλούμε αποθηκεύστε την εργασία σας, και πραγματοποιήστε επανεκκίνηση εντός του καθορισμένου χρόνου."
+ TimeRemaining = "Εναπομείναντας χρόνος:"
+ Title = "Απαιτείται επανεκκίνηση"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} θα συνεχίσει αυτόματα σε:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Εφαρμογή {0}'
+ DialogMessage = 'Αποθηκεύστε την εργασία σας πριν συνεχίσετε, καθώς οι ακόλουθες εφαρμογές θα κλείσουν αυτόματα.'
+ DialogMessageNoProcesses = 'Επιλέξτε Εγκατάσταση για να συνεχίσετε την εγκατάσταση. Εάν σας έχουν απομείνει αναβολές, μπορείτε επίσης να επιλέξετε να καθυστερήσετε την εγκατάσταση.'
+ ButtonDeferRemaining = 'παραμένουν'
+ ButtonLeftText = 'Αναβολή'
+ ButtonRightText = 'Κλείσιμο εφαρμογών & εγκατάσταση'
+ ButtonRightTextNoProcesses = 'Εγκαταστήστε το'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBzQVkGfnUexfiT
+# NBvC89TosWtdfUitMeBU+jktTZCm56CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgNVq7fajgkGypBLCA0a3kBiP+95o1Thctf6NTePY5Ucow
+# DQYJKoZIhvcNAQEBBQAEggGAGO0f/msnBtrn8smu8uhnFhO9wuVSjBrRLIq1ptgo
+# k+5mmKMjxCsCwsndKOCal0jm8Nu0Xf34/H+qWP3xWYVA2q/htnmHThSx2iSESAnD
+# DjvB4gkSFaoF6dFoxqDow5vlzpfiKDppD6wjdHSdkoEooqJNfGXg6ldNbJRh/CB0
+# 7iM9Yn21HB0u74Cmp23GMTTvAO35tuDuIpAzBE0wmYaww/GY6TmWCCYMfmKH7xBg
+# 89aACnggM5VqBihzWacsiKJyMuczC9L6cPCQRpOXKHvi/RFGeej2boXqH51XR9Nu
+# PNX1lFM8cQA0MQjiYnt+oXI7TmBHAp6xFCu67UoycNxJVq/Hnp8wbELcZhzx/Im5
+# AF13HkP/mBBwReiO0gicDoZ2XCyXf3A2a63qFmfi2f0iR8lSUBpGyYrPCAU4XvO7
+# X53FiWkH5l15ZgYOM3YdVe5BJkTElxBCB10rgIJuV2cjqBSLaekWNbrcB3XNIqlN
+# Jrm3AnWIk8UHSryNqm4CrO6BoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIH0NoHUW
+# riU7GRzk2TFACdMESuwyGYcMFMCTwCdsDik3AhBzGBBlBYt/NE2y+u9kJveiGA8y
+# MDI1MDIyMzIzNDczN1qgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDczN1owKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIO5zP47VFPCIEmqle+lYfrX62Yljf5NIEAn5sg0K
+# TV6qMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAHDkWgQw+dzKwjaQWZDgEgC5
+# JOW9gJraKz5L+65jyU2lJC7Tasp5FtBL7xvauTioRW0CzC/L0MFiBJvlTsZ3BBpj
+# iK6Jo96mf9heDxr6zh04J20WKGpqEoeyZ3veTFcvPVGZM7dX0LLWZA2H3yVh6mh8
+# KjWn+p8Km2p8QsCrXPtIMcfVzT+wcM3oeLMoNAGGHi8GTClQL96v4nndkMQc7qdK
+# 5wcIW7RN2doUXOYekknn2wSfFtdHOG7FE83NyC5k/C79FaHd+aprqJgOvhPmWr4u
+# 8QCTtztZcDx9GeyYNytZHUJJwZYDp1NUlp8ziWJSY2pEGoRjF/hwU32cYGxGCTjL
+# IJcXkqE9O2MgAvmzrEPO3Gh8lKdUXPt83bKzcvzvBdGHm9RVNwxMAxPU22+sonp6
+# G6IBPuhIovDxJ7Ape8IAcQ2i0MY94Elrp7P2ouW6sxG5zGXhXRVgCEcBfgptVWcj
+# hr5Tmzid/l2ATbfDnaewrzLFbvnS1/hS37/8U7JbUdGIyEfqcCwhC70S8HEW0P+x
+# UKSTqhSLk5WVKwA/NaFbtbQo9MI0S8tdPuumXnl7b2PCc3CgrGgehvmHSJKpwBP0
+# Y/MuRV5pdaJIsdRkbifgITNrtzYOrxu6Xj1L2il8Z7EAdSXBaT9xZgZOAcTYxXWs
+# x0cIYXouRmbtXVr4F5lb
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/es/strings.psd1 b/PSAppDeplyToolkit/Strings/es/strings.psd1
new file mode 100644
index 0000000..307374a
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/es/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "completada."
+ Error = "fallida."
+ FastRetry = "incompleta."
+ RestartRequired = "completada. Se requiere un reinicio."
+ Start = "iniciada."
+ }
+ BlockExecution = @{
+ Message = "La ejecución de esta aplicación se ha bloqueado temporalmente para que se pueda completar una operación de instalación."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Cerrar Programas"
+ ButtonContinue = "Continuar"
+ ButtonContinueTooltip = "Solo seleccione `"Continuar`" después de cerrar la(s) aplicacion(es) de la lista."
+ ButtonDefer = "Aplazar"
+ CountdownMessage = "NOTA: El/los programa(s) se cerrará(n) automáticamente en:"
+ Message = "Los siguientes programas deben estar cerrados antes de que la instalación pueda continuar.`n`nGuarde su trabajo, cierre los programas y luego continúe.`nAlternativamente, guarde su trabajo y haga clic en `"Cerrar programas`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Fecha tope:"
+ ExpiryMessage = "Puede optar por aplazar la instalación hasta que expire el aplazamiento:"
+ RemainingDeferrals = "Aplazamientos restantes:"
+ WarningMessage = "Una vez vencido el aplazamiento, ya no tendrá la opción de aplazar."
+ WelcomeMessage = "La siguiente aplicación está a punto de instalarse:"
+ }
+ DeploymentType = @{
+ Install = "Instalación"
+ Repair = "Reparación"
+ Uninstall = "Desinstalación"
+ }
+ DiskSpace = @{
+ Message = "El espacio en disco es insuficiente para completar la instalación de:`n{0}`n`nEspacio requerido: {1}MB`nEspacio disponible: {2}MB`n`nPor favor, libere suficiente espacio en disco para continuar con la instalación."
+ }
+ Progress = @{
+ MessageInstall = "Instalación en curso. Por favor, espere..."
+ MessageInstallDetail = "Esta ventana se cerrará automáticamente cuando finalice la instalación."
+ MessageRepair = "Reparación en curso. Por favor, espere..."
+ MessageRepairDetail = "Esta ventana se cerrará automáticamente cuando finalice la reparación."
+ MessageUninstall = "Desinstalación en curso. Por favor, espere..."
+ MessageUninstallDetail = "Esta ventana se cerrará automáticamente cuando finalice la desinstalación."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimizar"
+ ButtonRestartNow = "Reiniciar Ahora"
+ Message = "Para que la instalación se complete, debe reiniciar su equipo."
+ MessageRestart = "El equipo se reiniciará automáticamente al final de la cuenta regresiva."
+ MessageTime = "Por favor guarde su trabajo y reinicie dentro del tiempo asignado."
+ TimeRemaining = "Tiempo restante:"
+ Title = "Reinicio Requerido"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "La {0} continuará automáticamente en:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Aplicación {0}'
+ DialogMessage = 'Guarde su trabajo antes de continuar, ya que las siguientes aplicaciones se cerrarán automáticamente.'
+ DialogMessageNoProcesses = 'Seleccione Instalar para continuar con la instalación. Si le queda algún aplazamiento, también puede optar por retrasar la instalación.'
+ ButtonDeferRemaining = 'permanezca en'
+ ButtonLeftText = 'Aplazar'
+ ButtonRightText = 'Cerrar aplicaciones e instalar'
+ ButtonRightTextNoProcesses = 'Instale'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCSv3EojKc6tsgS
+# 3Kz7UyAzX+5TKxthEdOzDW26U2hBBqCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgMl9As6miCR3zANSLv1p87zYrJ7xoumJEVZJEBqRqWhkw
+# DQYJKoZIhvcNAQEBBQAEggGAopwSChR+AZr77UCIMkDojybUKRlreXwppaqC6B/b
+# 2j/22Hv9CaJt+kKMG0eNHwhf3N5fy7qU0bla15BToZQJgHbVlvbGeKxTr26X1A0l
+# vVJ+zlehGtHJiJqE8ixuKCTZ9pj086f2yh2Cx/90ms1Mh2rwYpI+ZzBZdYHCrUfr
+# XBip9FpLYOocTs5TdjLFJxj+G4tWusNpsU+KO+RdP6JI+pivmEgPWJpU1MNh14j5
+# BBkjzhQ0uz4HUt4HbR/W67hsxFx/kHGsag/IMLM7P7QwVMEwcHrox07n5dyqan66
+# diM58+xeHM0uLhRwKiXXMdVvgZwYbLXdgvW4m9fxWnlBjQJlUiabzrxt+wuXr3Jd
+# eb/Ybr1ejFj1c/wTbHWmMgtplHGFtAQ6rIhQ9qRfS7a5Rz7Da2RgHNpOXQlnNx1I
+# THkttCxyoOrml9IxfPEvQ2JCN9iDkCjWJOZGSVTGNRvoex6bN2/o2bCB2w0TvldQ
+# qFkslA4azWN7UTWcAPVb7bvooYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIOTu8EJT
+# jyG8zG9VBslZuzT59TCpmq8t7m22MwyzpAmPAhEA0wM9x1BtHjxU6fxFABQvaBgP
+# MjAyNTAyMjMyMzQ3NDBaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3NDBaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCCDnP86nOK3qERhAaQTxoPhPnGvCAq/g2ZPauRa
+# SVoYyjA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgChDvPtOyOzMFRUqrfKO3an
+# W++b3aiMWvEvVUEDM1LgDwvSmMC9wNTogRtLRkVTDzMxCwDdg4UE6U1awzAAGrS1
+# 4Cg3Zqk+SPk08EaHLk7kunKDzTT3sxYtqRsAUEwe4+p0tRGgWFejHW7e6sQP53HZ
+# 61nhWJCuwVhuM7L4i6El2LgjIIFHCwAOE+f/G7Nrss6v+zDC/8KZ1eFcE7yUsvkN
+# s8rafIubcPd5cGxNE+Wzb/O7Pp0Lo9zpytmdyghozHpNHREETcBPRGWAgmArhaqZ
+# 8GoGtmxKYp35t9uBJ1N3ae+oNsTmgI1jeAWw2IUMiIaQGNy47Cn48fq0Fr1t5sU7
+# uQ5ro0grWNIzfg0OA7Y3PZigeQGN2sl/e2TgaARC90soC/OcN/ajt05MCc5pNi7d
+# xYPJXLSjyPXy8trpD1v0kvzkTZ7cByxZWP7NNimHXYMEHc7mO9nDSFo2uWL3IkPy
+# Iu1G1Z0AUbwHLMTWGT69wpvCIxbgUWT/+Yb+b+d7I8Lq9nkJ63xAOwr4avkTyExM
+# LYektrszxaMQSMICv3uxVWAs+EeCjc+/IS0ZZIkNVkFuYiNh0JzLn4dJ7tes+/kO
+# h5m3+pjK8Cef9x4brzQI9cOezoArOk1QgfoQbKNTwaYibnQzM5IF0kgzn0cZT4W4
+# hkXDleGA4OEL+FrHSyr2MA==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/fi/strings.psd1 b/PSAppDeplyToolkit/Strings/fi/strings.psd1
new file mode 100644
index 0000000..e897386
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/fi/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "valmis."
+ Error = "epäonnistui."
+ FastRetry = "ei ole valmis."
+ RestartRequired = "valmis. Tietokone on käynnistettävä uudelleen."
+ Start = "alkoi."
+ }
+ BlockExecution = @{
+ Message = "Ohjelmiston käynnistäminen on tilapäisesti estetty, jotta ohjelmisto voi onnistuneesti asentua."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Sulje ohjelmat"
+ ButtonContinue = "Jatka"
+ ButtonContinueTooltip = "Valitse jatka, kun olet sulkenut ohjelmat."
+ ButtonDefer = "Myöhemmin"
+ CountdownMessage = "HUOMIO: Ohjelma(t) suljetaan automaattisesti:"
+ Message = "Seuraavat ohjelmat on suljettava ennen asennusta`n`nTallenna työsi ja jatka. Vaihtoehtoisesti voit tallentaa työsi ja valita `"Sulje ohjelmat`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Määräaika:"
+ ExpiryMessage = "Voit siirtää asennusta myöhemmäksi:"
+ RemainingDeferrals = "Jäljellä olevia siirtoja myöhempään ajankohtaan:"
+ WarningMessage = "Tietyn ajan kuluessa et voi enää siirtää asennusta myöhemmäksi."
+ WelcomeMessage = "Ohjelma joka asennetaan seuraavaksi:"
+ }
+ DeploymentType = @{
+ Install = "Asennus"
+ Repair = "Korjaus"
+ Uninstall = "Ohjelmiston poisto"
+ }
+ DiskSpace = @{
+ Message = "Kiintolevyllä ei ole riittävästi tilaa asennusta varten:`n{0}`n`nVaadittu levytila: {1}MB`nLevytilaa käytettävissä: {2}MB`n`nVapauta levytilaa, jotta asennus voi jatkua."
+ }
+ Progress = @{
+ MessageInstall = "Asentaa. Odota..."
+ MessageInstallDetail = "Tämä ikkuna sulkeutuu automaattisesti, kun asennus on valmis."
+ MessageRepair = "Korjaus käynnissä. Odota..."
+ MessageRepairDetail = "Tämä ikkuna sulkeutuu automaattisesti, kun korjaus on valmis."
+ MessageUninstall = "Ohjelmistoa poistetaan. Odota..."
+ MessageUninstallDetail = "Tämä ikkuna sulkeutuu automaattisesti, kun asennuksen poisto on valmis."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Käynnistä uudelleen myöhemmin"
+ ButtonRestartNow = "Käynnistä uudelleen heti"
+ Message = "Tietokone on käynnistettävä uudelleen, ennen kuin ohjelmiston asennus on valmis."
+ MessageRestart = "Tietokone käynnistyy uudelleen, kun laskuri on saavuttanut nollan."
+ MessageTime = "Tallenna työsi ja käynnistä tietokone uudelleen aikarajan sisällä."
+ TimeRemaining = "Aikaa jäljellä:"
+ Title = "Tietokone on käynnistettävä uudelleen"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} jatkaa automaattisesti:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Sovellus {0}'
+ DialogMessage = 'Tallenna työsi ennen kuin jatkat, sillä seuraavat sovellukset suljetaan automaattisesti.'
+ DialogMessageNoProcesses = 'Jatka asennusta valitsemalla Asenna. Jos sinulla on vielä lykkäyksiä jäljellä, voit myös lykätä asennusta.'
+ ButtonDeferRemaining = 'pysyä'
+ ButtonLeftText = 'Siirrä'
+ ButtonRightText = 'Sulje sovellukset & asenna'
+ ButtonRightTextNoProcesses = 'Asenna'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBvOId8AL99saKk
+# VvVRwPivC7Hbo2Fj2GQqc8hG2/pl8KCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgmffwpaWDSby3MZgvaEhLndZNKRA9jqMIn+0en/M161Uw
+# DQYJKoZIhvcNAQEBBQAEggGACzm/KUBdZQWE1e6fIJ3nVsdQFSQ83W21jwl3Mp5o
+# +AlbhKEwjQgtU+nGGC7gw9ZIXxu2yDYyp3An1iSoXpQ0Ty1muUbcVWx70PEDXrGT
+# nIjWAUX8G4wHPF6UPfUZvouHRZFmUcV8h4vIK2H2OpiYQGf+QSUWokg1s3d18JoR
+# rV0Jul/6R7guIaEHt2eTo9fM1ZHhg08wyY5iyOjvZRAvpnhKgDiM33t8NxncMI1V
+# KDZXUt+4SQ5zif0wqh1PcbTTXQ7V+Qb9NCaetNzjbTM8f7S8jVVx6BkgLFsJ15CT
+# JiZJBj2FR+vuuZLUmKZ+vNaBi9gMVkjU1n7mazUvM7skGLtYD1th5By5gjJXyySS
+# aV6+8Db0YgpUGGmeGAXGQreePGFMg+E/cwLQBVWpN+9t0B1euKRq35MUI5u1VoyM
+# c/NM1vqv79dUCIIRppl38vaR0yFEwNbx8RI2bM8pTy+YEXzltqtXROMdjNoydEEu
+# /RpV44sr6gFhUzcbsS3KO7lfoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIJcPKPZX
+# F3Vhecwdw0hIE4jonViPr5X47ne2Nkoy0P13AhEAjT4WnBECnWuYvg4L08I2ARgP
+# MjAyNTAyMjMyMzQ3NDNaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3NDNaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCATxtvMl9mJhJNZAM8uHL8kwumw5u4C1qua3J1s
+# snhMWjA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgATafX+AIsxB3qgLfgi9AMj
+# +G8tQ0fRU0OXzqb5J925NbpzFWxeeduh38tZT3q9sANo+FxA/ugUWYiBzrKhUtbN
+# bluq26qyT9t6w2+BatCUjxUgs0k5k0JpqUoo65kRhRolGHg6s0UX1HOBCHM4Eg59
+# RclxBYa/8Fahp6s4++GOzb/Kkb5MSu2vMSSDcmQN7WZy7N9mrgC9wZ42A17zZjPv
+# tfj+5oezMn8C7DDlUMYArWegK5/Ihm7+/Pw2NPaJHq39IRv5COETdkRXDaZR9+SS
+# xQDBgSnSbDj3e4nXttld53IZjNPsCvVC3TboNP0ghh/YbCk/vIuG007lQoKDfYwl
+# bG05XnC0PPWRghtRGTsl+UF96dqfgTP/Kx0k09m4GdvlbJAr2m+TWDB7BmO7HgPI
+# 8YDfk9bfQxVf5Blprm2RhxGcPscYY18vXsbAYaxhADsEUANz1zHoevO+VWX5gplv
+# GPVvhMxRf650pfRU/ChB6W8gpo2CUN3eXfrLSprmIacXRrgHm3+2j3Z2/zrtrIAB
+# i0LTf2kH4OqwNUS2NgYuD+niMquIzQWXqfKKWDA/3PaPOdLzbY1I0izQq//jkEgJ
+# UytZL5D9xzVdwxcrrYCAQlqo14Z/89AlAfvW7SQfqkzsXpEeU9dpKdSDL3bPp/fn
+# LbwacoZEtmzYlZRqjOEEUg==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/fr/strings.psd1 b/PSAppDeplyToolkit/Strings/fr/strings.psd1
new file mode 100644
index 0000000..e3fdc40
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/fr/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "réussie."
+ Error = "en échec."
+ FastRetry = "incomplète."
+ RestartRequired = "réussie. Un redémarrage est requis."
+ Start = "en cours."
+ }
+ BlockExecution = @{
+ Message = "Le lancement de cette application a été temporairement bloqué afin qu'une autre installation puisse se terminer."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Fermer Programmes"
+ ButtonContinue = "Poursuivre"
+ ButtonContinueTooltip = "Veuillez cliquer sur « Poursuivre » uniquement après avoir fermé la ou les application(s) ci-dessus."
+ ButtonDefer = "Reporter l'installation"
+ CountdownMessage = "REMARQUE: Les programmes seront automatiquement fermés dans:"
+ Message = "Les programmes suivants doivent être fermés afin que l'installation s'initialise.`n`nMerci de sauvegarder votre travail, fermer tous les programmes, et continuer. Vous pouvez aussi sauvegarder votre travail puis cliquez sur « Fermer Programmes »."
+ }
+ DeferPrompt = @{
+ Deadline = "Temps limite:"
+ ExpiryMessage = "Vous pouvez choisir de reporter l'installation:"
+ RemainingDeferrals = "Nombre(s) de report restant(s):"
+ WarningMessage = "Quand le temps aura expiré, vous n'aurez plus la possibilité de reporter."
+ WelcomeMessage = "L'application suivante est sur le point d'être installée:"
+ }
+ DeploymentType = @{
+ Install = "Installation"
+ Repair = "Réparation"
+ Uninstall = "Désinstallation"
+ }
+ DiskSpace = @{
+ Message = "Vous n'avez pas assez d'espace sur le disque pour compléter l'installation de:`n{0}`n`nEspace requis: {1}MB`nEspace disponible: {2}MB`n`nMerci de vous assurez d'avoir assez d'espace libre pour pouvoir continuer l'installation."
+ }
+ Progress = @{
+ MessageInstall = "Installation en cours, merci de patienter..."
+ MessageInstallDetail = "Cette fenêtre se fermera automatiquement lorsque l'installation sera terminée."
+ MessageRepair = "Réparation en cours, merci de patienter..."
+ MessageRepairDetail = "Cette fenêtre se fermera automatiquement lorsque la réparation sera terminée."
+ MessageUninstall = "Désinstallation en cours, merci de patienter..."
+ MessageUninstallDetail = "Cette fenêtre se fermera automatiquement lorsque la désinstallation sera terminée."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimiser"
+ ButtonRestartNow = "Redémarrer Maintenant"
+ Message = "Pour que l'installation soit compléte, vous devez redémarrer votre ordinateur."
+ MessageRestart = "Votre ordinateur sera automatiquement redémarré à la fin du décompte."
+ MessageTime = "Merci de sauvegarder votre travail et de redémarrer avant que le temps spécifié ne soit écoulé."
+ TimeRemaining = "Temps restant:"
+ Title = "Redémarrage Requis"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "L'{0} va continuer automatiquement:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - App {0}'
+ DialogMessage = 'Veuillez sauvegarder votre travail avant de continuer, car les applications suivantes seront automatiquement fermées.'
+ DialogMessageNoProcesses = "Veuillez sélectionner Installer pour poursuivre l'installation. S'il vous reste des reports, vous pouvez également choisir de retarder l'installation."
+ ButtonDeferRemaining = 'rester'
+ ButtonLeftText = 'Report'
+ ButtonRightText = 'Fermer les applications et installer'
+ ButtonRightTextNoProcesses = 'Installer'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDVKYvHmgSSAij6
+# +9Toy4tkxvDPQxXcuvZF602QdSVH66CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQg3cFrEoFW1SpCVUskIPEHZvXT/pDvZZRIrEUk/DrgRCMw
+# DQYJKoZIhvcNAQEBBQAEggGAtX6rTnEg9ZCcKMTiMbqDngJUmKWzx0s814citcFA
+# QbGjdy/oG7NGbtL8Gg3/2MkXoS5ig2W0pSkztT+LNHXCqo5MQCw9aPL2BGvsAx54
+# A9ye1VSnYAW/FdUHRlqgR4fg9H/NOJpo96n3z4t3Dz5suF+ji8N8hwslA271lMHy
+# IbuNzCTA27grvyxI2XVWEo5Jozze7y1tZutEXBp6iX9i5wSlWK304ylAKW09E2Zs
+# KKVlDblw0rPq3cBc/jebJAsCVDbr+PoE2uvUYPFvZqv3HNI2r88gUpSBDraRW8p/
+# hlmdzE0QnWsG+MJMNbOwrelKASYnxhnHtO6qp0J4236uesuSblPopqgDdpOw/ui8
+# /EAAdJkreEeRVMZ5mmrGRxS0oFqOI3cthmQW9a0YtyZloF0bLF1ii+LLSOI4OWSG
+# jN52tl97K8ZOirKMVLEN1yjt0Os7xKJAwskZxcjnadmuVYNoNV6g0gO90vNIVhjZ
+# RNkvkk0Vs/yEk6yQoDN1RrZKoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEINwDywJn
+# qVlxPFqbpAvl4XCd6ToL8zHe7pjn6AixJ6sEAhAnM9tuQf2QGuLGEiyO/nmrGA8y
+# MDI1MDIyMzIzNDc0NVqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDc0NVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIMZrxmfCelM42FAwR2ZUjJ4arWlJe57eaOszjmpj
+# zQT4MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAGsOMAAy45xC7s6gTQG7ofCM
+# cGe8bee0lXzdiY1Bd2bUbDz2jDGI/I2UE0qZYbPoykAc84i5xkeWO1HELMnITC+P
+# h59+AYaOmTqEUHS7wqpFJLK+68vwZXG14DrrLnXOt1NRc2JqcK4FLE0soxQLcY0O
+# 4++/LVK2eZXld1Q84fMbpCUVodjdB6b/UzbYX78WMtdIoqIQfTHbYZMWowm60slW
+# dly1ozFOgB+GtLhixHHRQqwGlMBNTJLPWWu5Z8RAR/f5V5mBCVCuthApE+lNbjoJ
+# E2WiC8b/eWwDCN02RhDILxNTmJp61SDmqq6wP1t8bVnb0xsp7siGDfkXTQ2HwpK2
+# ILeiE7rsmA6LuUCFXbY7vFzzQ1i0c5ix+7MXjAazkSooxeu2+T9hzs8zwEdy6ZmE
+# VlGHTTf/hRHJPkdTLAk3tbcRe2tbDVlnwjvO+/ltTL9qBid28p3YXP9qLwnBzxOs
+# FvKk3Ix8SMqXBc4ZymfgpxQYK7+FpOisrUNgZabA7Ybxoy8g5YSeFREx3DDGvCZt
+# BcDcUlFvPpEDwjIDnbsQpQl2dIG4YtL2eCtFpppO0ZsB1gX9wgDc9ekVbRsAXnr5
+# 0aU57hepuxFs0R48+Sub+t1XCwCziVZModc7LvOAcRZiFI1OK241jT/an5H9lymT
+# Uk2zeA+K57vTutRyo+hk
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/he/strings.psd1 b/PSAppDeplyToolkit/Strings/he/strings.psd1
new file mode 100644
index 0000000..8c4bd94
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/he/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "הושלמה."
+ Error = "נכשלה."
+ FastRetry = "לא הושלמה."
+ RestartRequired = "הושלמה. נדרש אתחול המחשב."
+ Start = "התחילה."
+ }
+ BlockExecution = @{
+ Message = "הרצת היישום הזה נחסמה זמנית כדי שפעולת התקנה תוכל להסתיים."
+ }
+ ClosePrompt = @{
+ ButtonClose = "סגור תכניות"
+ ButtonContinue = "המשך"
+ ButtonContinueTooltip = "בחר `"המשך`" רק לאחר שסגרת את היישום(ים) הרשומים לעיל."
+ ButtonDefer = "דחה"
+ CountdownMessage = "שים לב: התכנית(ות) תסגרנה באופן אוטומטי תוך:"
+ Message = "יש לסגור את התכנות הבאות בטרם ההתקנה תוכל להתחיל.`n`nאנא שמור על העבודה שלך, סגור את התכניות, ואז המשך. לחילופין, שמור על העבודה שלך והקלק על `"סגור תכניות`"."
+ }
+ DeferPrompt = @{
+ Deadline = "תאריך יעד:"
+ ExpiryMessage = "אתה יכול לבחור לדחות את ההתקנה עד שמשך זמן הדחיה יפוג."
+ RemainingDeferrals = "מספר הדחיות שנותרו:"
+ WarningMessage = "לאחר שמשך זמן הדחיה יפוג, לא תהיה לך עוד אפשרות לדחות."
+ WelcomeMessage = "היישום הבא עומד להיות מותקן:"
+ }
+ DeploymentType = @{
+ Install = "התקנה"
+ Repair = "תיקון"
+ Uninstall = "הסרה"
+ }
+ DiskSpace = @{
+ Message = ":אין לך מספיק מקום בכונן כדי להשלים את ההתקנה של:`n{0}`n`nמקום נדרש: {1}מ`"ב`nמקום זמין: {2}מ`"ב`n`nאנא שחרר מספיק מקום בכונן כדי להתחיל בהתקנה."
+ }
+ Progress = @{
+ MessageInstall = "מבצע התקנה. נא להמתין."
+ MessageInstallDetail = "חלון זה ייסגר אוטומטית עם השלמת ההתקנה."
+ MessageRepair = "מבצע תיקון. נא להמתין."
+ MessageRepairDetail = "חלון זה ייסגר אוטומטית עם השלמת התיקון."
+ MessageUninstall = "מבצע הסרה. נא להמתין."
+ MessageUninstallDetail = "חלון זה ייסגר אוטומטית עם השלמת הסרת ההתקנה."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "מזער את"
+ ButtonRestartNow = "אתחל עכשיו"
+ Message = "כדי להשלים את ההתקנה, עליך לאתחל את המחשב שלך מחדש."
+ MessageRestart = "המחשב שלך יאותחל באופן אוטומטי בסיום הספירה לאחור."
+ MessageTime = "אנא שמור על העבודה שלך ואתחל במסגרת הזמן המוקצב."
+ TimeRemaining = "הזמן הנותר:"
+ Title = "נדרש אתחול המחשב"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "ה {0} ימשיך באופן אוטומטי:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - אפליקציה {0}'
+ DialogMessage = 'אנא שמור את עבודתך לפני שתמשיך שכן היישומים הבאים ייסגרו אוטומטית.'
+ DialogMessageNoProcesses = 'אנא בחר התקן כדי להמשיך בהתקנה. אם נותרו לך דחיות, תוכל גם לבחור לדחות את ההתקנה.'
+ ButtonDeferRemaining = 'לְהִשָׁאֵר'
+ ButtonLeftText = 'לִדחוֹת'
+ ButtonRightText = 'סגור אפליקציות והתקן'
+ ButtonRightTextNoProcesses = 'לְהַתְקִין'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCB7HeBhRpQexV8N
+# /ABmEgEm4/DIyMgvQgHEviy07rMN46CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgS0mMDZ3vnhvUylaeeXoaQ0U+ZVMc2FVIaQkbGbAeFb4w
+# DQYJKoZIhvcNAQEBBQAEggGAuL/jG16716SjUTYJvB/CK3Z4yi4HhITXHWFl0Zhp
+# liHeokku8A1eA5NFx09MXjf//jVrIt0v7tEljwE34+XZ0jMT1jbyJ9BcquKbrcl6
+# MnNSFayqu83OuvzfKyNbY8kmFU6n6fOhi8Y19HZC0SFfVB6AOr/gxtkjyvnZ81pH
+# 5GghfZ2Cu+ZKA2HCWNoWBR00zhUMLSjQO7emiLnZa7hgYKONhj5PE7TbPNZtAGTU
+# qlEnZY4g7X6aK4TnU5vyKhbYkgINtg9fmT5cFt6u3ofPxfpuiYsWdWJwFkYkVmMs
+# ZXvE+GqVURXpluPM3X6ukLCIOnnZt7BBXl1bnbfzFsr9ux8PNG6+oMK6fUb87sIc
+# uPp9xmrGIeKhV0T5dzFgn6iJydOW3IG/R8InJEBPYbVFXGnyMNtkNX8cONRmCNND
+# MpKpcXeS1DYJVCmO9ixEbZ9Z2TDYJOfD3yYtlOgTpAK+7PRbQNlXJnKEbbvdNqoK
+# nq76AuSje6sNxP/3v8a43H4doYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIPI07HwG
+# 30RYIYZkOOH/zJzUbLqp6j3oAF6XiLvfansDAhA/11D+V2AzBWHftPcVjiQAGA8y
+# MDI1MDIyMzIzNDc0OFqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDc0OFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIMMhtx/fWpSo9trtKYG/Nk112LKZuOhgtr+0/xGr
+# PG5rMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICABciijVVMj1nm3o5p+0CF4DI
+# 0U2B7dPuFluv3QxV9ITYMw8WNhfFuEYCEferbiY2EzKwWKDhrMJ7CO5E2NKRVIGm
+# 7sQP/P3om9WmWWGdWjQWq/vkMXOw0RG17BepIkmW+yfIxiSNiknFU57ShM1Ps4V4
+# j3+IhM/Rik9rdgAyoELiVCq6quPCFlb/pyxo93HlOyh2bRcqek2Qe/2f+xTzT72M
+# IpIkwk8W2ZheHFMblb8/42bQldIl3gu0Aa5PHnvqAiCpOh+BW16I6ipFJxpIAdng
+# Urxpwml887/j0yHD3mwZUMU9clt7XC2Xf68YI2SiIM8/RZwHaQwDn2Psxa9fQ0an
+# iab08J4indYW/htIbRG05nsIeuqmH59iyl6vyQgm9VJNUvHDA+tBrQ+gusAIlC3X
+# HOQOx8phWJSKjw+SnmGJPA60e/MQXGeCNjOuea2dRtP0VfNWp4Uj8MEr328/uOqP
+# uHV6AR1KN5447fB+s/d22ZyuqM2oiLelKtWALnDKUfpTFx1OjD8jVov1i2MsV4RJ
+# 1UDKW005f0vTRgXDFzRf9mKYeeGkLtwC/lFZqQp1XtzacgDoYXE4V6Zne8j8xokC
+# 8qfaZVA63a+yyDizqPwTT8XFr0eP8EcPTZf35DcHs5LDoqqvmbHpIeddwFoVprSB
+# C3vF7UFpV8RoVBwiKvsy
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/hu/strings.psd1 b/PSAppDeplyToolkit/Strings/hu/strings.psd1
new file mode 100644
index 0000000..8266f3e
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/hu/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "elkészült."
+ Error = "sikertelen."
+ FastRetry = "nem lehet befejezni."
+ RestartRequired = "elkészült.Újraindítás szükséges."
+ Start = "elindult."
+ }
+ BlockExecution = @{
+ Message = "A következő alkalmazások blokkolva lesznek, annak érdekében hogy a telepítés problémamentesen végrehajtódjon."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Alkalmazások bezárása"
+ ButtonContinue = "Tovább"
+ ButtonContinueTooltip = "Csak azután kattintson a `"Tovább`"-ra, ha a fentebb látható alkalmazás(oka)t bezárta."
+ ButtonDefer = "Elhalaszt"
+ CountdownMessage = "Megjegyzés: a programok automatikusan bezárásra kerülnek,:"
+ Message = "Az alábbi programokat szíveskedjen bezárni, mielőtt a telepítés elkezdődik.`n`nKérjük mentse munkáját és a folytatáshoz zárja be a futó alkalmazásokat. Vagy Kérjük mentse munkáját és kattintson a `"Programok bezárása`"-ra."
+ }
+ DeferPrompt = @{
+ Deadline = "Időpont:"
+ ExpiryMessage = "A telepítést elhalaszthatja amíg a rendelkezésre álló idő lejár:"
+ RemainingDeferrals = "Fennmaradó halasztás:"
+ WarningMessage = "Amennyiben a rendelkezésre álló idő letelik, nem lesz lehetősége a telepítés elhalasztására."
+ WelcomeMessage = "A következő alkalmazások telepítésre kerülnek:"
+ }
+ DeploymentType = @{
+ Install = "Telepítés"
+ Repair = "Javítás"
+ Uninstall = "Eltávolítás"
+ }
+ DiskSpace = @{
+ Message = "Nincs elég lemezterület a telepítés végrehajtásához:`n{0}`n`nSzükséges lemezterület: {1}MB`nSzabad lemezterület: {2}MB`nKérem szabadítson fel elegendő lemezterületet a telepítés végrehajtásához."
+ }
+ Progress = @{
+ MessageInstall = "Telepítés folyamatban. Kérem várjon..."
+ MessageInstallDetail = "Ez az ablak automatikusan bezáródik, amikor a telepítés befejeződik."
+ MessageRepair = "Javítás folyamatban. Kérem várjon..."
+ MessageRepairDetail = "Ez az ablak automatikusan bezáródik, ha a javítás befejeződött."
+ MessageUninstall = "Eltávolítás folyamatban. Kérem várjon..."
+ MessageUninstallDetail = "Ez az ablak automatikusan bezáródik, amikor az eltávolítás befejeződik."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimalizál"
+ ButtonRestartNow = "Újraindítás most"
+ Message = "A telepítés befejezéséhez a számítógépet újraindítása szükséges."
+ MessageRestart = "A hátralévő idő leteltével a számítógép újraindul."
+ MessageTime = "Kérem mentse munkáját, és a megadott időn belül indítsa újra.."
+ TimeRemaining = "Hátralévő idő:"
+ Title = "Újraindítás szükséges"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "A(z) {0} automatikusan folytatódik:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Alkalmazás {0}'
+ DialogMessage = 'Kérjük, mentse el a munkáját, mielőtt folytatná, mivel a következő alkalmazások automatikusan lezárulnak.'
+ DialogMessageNoProcesses = 'Please select Install to continue with the installation. If you have any deferrals remaining, you may also choose to delay the installation.'
+ ButtonDeferRemaining = 'maradjon'
+ ButtonLeftText = 'Elhalasztás'
+ ButtonRightText = 'Alkalmazások bezárása és telepítése'
+ ButtonRightTextNoProcesses = 'Telepítse a'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAOXIdFYpGTUGzW
+# FCi7Ug/Rfmoqxzq1cDL3SVYL3aMIYKCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgm4yGhRwwoyOJcTPAcizWuerp7kaA0kbj/Beyz9dtcmkw
+# DQYJKoZIhvcNAQEBBQAEggGAlwvaKzfqa+vQ2CbzJt9GxY6FwFM5x39GtQ/bLbja
+# H9SMF52ngvTIAlj/cA66sFajS9gp96Uln4ZOYQSiBcssPbJzCn0cGC/vg/1dGmep
+# t60Fgr290llIwM8tnxT+cpN5q26eYZ2QAv2geyhZI2hfgat/98rjA16LsF+5Ym4g
+# NsGBU8SJ5JlyCHb/rggvSUxk92GiWUa9apMnx3slQU57QG1gvQxTemUM/BdPH7+X
+# EL01znp7CfLI2aelmQsOcZekAQEPhF8z4Xy1MnpVU4VNIKEddRb4mAVFch19/pue
+# DKdywL2TzGWOlrBjDJPJhYJvN/qa6HyB6Qp3zy0R4N4DMzLBsVV84ulO7NcAo/Fx
+# H86OdNMitwGCiOZVpGqquq++Edoe0muezM5zbxBnAxNnch7/cM5SqpzsCKRNRhkZ
+# pOnrdY2gbUp/4g2H+PgIG0AaJEQXJCDFIX3wjyIsKLDX3mfGMwh5DDSQcq1Lrsvy
+# x14F9BDA2wd6/XV4JVviSuKgoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIO9K0ukn
+# kZEE1xzPpiJ8UMXAevGpM711jwgdwpwNPm9IAhBNDdjPeO3x7kZDgA4HSuYWGA8y
+# MDI1MDIyMzIzNDc1MVqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDc1MVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIDHnhsqaVWof91hIX4cI82Mkg7Cq2BhdrJA6k0Nx
+# vBvkMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAE+7QLnmUE9V1uR1z2rLNVq3
+# C+D4z4t0al3CmSXKVnu6HF+nojTX3wI/XIEDZOFBXBtFtzDfAWkUSQNYWk515026
+# KTErEkM7Up7T37QOsZoVCF8oquvox39SQbTomylxzCDWxhgBa81R1t9OSvszRKhN
+# BdyX/j1+deyJ6qeyvXb9cHdxCx3mYr9Xyo7JptuJr+CrCMRfJUS1So1z2MZJkPU8
+# NXCGA9gcXgjqolTKRqq3eutOBUW5zEAke3m5z04qLYbL4KSzduEHVvt7toAaqIdz
+# j9Ei40RfY4uXlzv/QmLoJcBHYB3nYzt87Sf0AFV0NFYi8KDREPI/V3GNGJc06ZQY
+# w4pJQFJ6VP6HZnWKKFyFjbukhNkUzLxPQ2PNUJizTGPrYsBeDMs45rbh3EAotmJ+
+# oNjE+Z+nUni/JSFoBvIdMg+WaQLn5qPdeNCTfUtmnueOl6DLFszMZ5fJ5sSW6Hne
+# 8G0rnS5ItH7hmB6KgO42OB0vIHNp5Szdm9tf1kTZgYZzoAoHaPfGCyGrrfhCdItU
+# HiW1J5mGkHz0LYpzNb/F+qX3QP2717qj7g9/DrfUrODzFV3gMdhuLxO9urBwbaY+
+# yfUCl90S98qEYO3kxoQbklsnyyyTStiOO10hfLPLzvOOmuay0SZOl26tdENpSmLI
+# 4z0oj8+W+URfrtiRsoGd
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/it/strings.psd1 b/PSAppDeplyToolkit/Strings/it/strings.psd1
new file mode 100644
index 0000000..02dd087
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/it/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "Completata."
+ Error = "Fallita."
+ FastRetry = "Non completata."
+ RestartRequired = "Completata. È necessario riavviare il computer."
+ Start = "Iniziata."
+ }
+ BlockExecution = @{
+ Message = "L'esecuzione di questa applicazione è stata temporaneamente bloccata in modo che l'operazione di installazione possa essere completata."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Chiudi Programmi"
+ ButtonContinue = "Continua"
+ ButtonContinueTooltip = "Seleziona `"Continua`" solo dopo la chiusura della(e) applicazione(i) elencate sopra."
+ ButtonDefer = "Rimanda"
+ CountdownMessage = "NOTA: il programma(s) sarà chiuso automaticamente in:"
+ Message = "I seguenti programmi devono essere chiusi prima che l'installazione possa procedere.`n`nSalvare il lavoro , chiudere i programmi, e poi continuare. In alternativa, salvare il lavoro e fare clic su `"Chiudi Programmi`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Scadenza:"
+ ExpiryMessage = "Si può decidere di posticipare l'installazione fino alla prossima richiesta automatica:"
+ RemainingDeferrals = "Posticipi rimanenti:"
+ WarningMessage = "Una volta che le richieste rimanenti saranno scadute, non sarà più possibile posticipare l'installazione."
+ WelcomeMessage = "La seguente applicazione sta per essere installata:"
+ }
+ DeploymentType = @{
+ Install = "Installazione"
+ Repair = "Riparazione"
+ Uninstall = "Disinstallazione"
+ }
+ DiskSpace = @{
+ Message = "Non si dispone di spazio su disco sufficiente per completare l'installazione di:`n{0}`n`nSpazio necessario: {1}MB`nSpazio disponibile: {2}MB`n`nSi prega di spazio libero su disco sufficiente per procedere con l'installazione."
+ }
+ Progress = @{
+ MessageInstall = "Installazione in corso. Attendere prego..."
+ MessageInstallDetail = "Questa finestra si chiude automaticamente al termine dell'installazione."
+ MessageRepair = "Riparazione in corso. Attendere prego..."
+ MessageRepairDetail = "Questa finestra si chiuderà automaticamente al termine della riparazione."
+ MessageUninstall = "Disinstallazione in corso. Attendere prego..."
+ MessageUninstallDetail = "Questa finestra si chiuderà automaticamente al termine della disinstallazione."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimizzare"
+ ButtonRestartNow = "Riavvia Ora"
+ Message = "Per completare l'installazione, è necessario riavviare il computer."
+ MessageRestart = "Il computer verrà riavviato automaticamente al termine del conto alla rovescia."
+ MessageTime = "Salvare il lavoro e riavviare entro il tempo assegnato."
+ TimeRemaining = "Tempo rimanente:"
+ Title = "Riavvio Richiesto"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "Il {0} continuerà automaticamente in:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - App {0}'
+ DialogMessage = 'Salvate il vostro lavoro prima di continuare, perché le applicazioni seguenti verranno chiuse automaticamente.'
+ DialogMessageNoProcesses = "Selezionare Installa per continuare l'installazione. Se sono rimasti dei rinvii, si può anche scegliere di ritardare l'installazione."
+ ButtonDeferRemaining = 'rimanere'
+ ButtonLeftText = 'Rinviare'
+ ButtonRightText = 'Chiudere le applicazioni e installare'
+ ButtonRightTextNoProcesses = 'Installare'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDg8/niiapzjO29
+# UL3NQsxxD6lE2VB5dGfbIaEnKWoXsqCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgdyOlSoAGFWsR9a5EmU6GBYyO2Z/4YBXwZWMZc8+x5m0w
+# DQYJKoZIhvcNAQEBBQAEggGAir+uIn2+gpS4vBbA+6Cfwq9DjFaC37Ioh85QnXrb
+# UUh+V05ptHhfZRFrnPvlAQFC+89POuCb4ZrPIl0t1nSucKAUQL3y0vOvY8wXHnqn
+# 07ggZeJtckFOuR6EacfFCx/CQXO1MRpz8KClZ4beyoFEpl2NGd7gCD+aH+eIzK92
+# RILr65Smo56EzlpoPTNYH9ealTriTnH0m3SDAlFjr4cHfmY4+Cw462iu273zHU9a
+# 3MDEph6x+B8D7+JuE8lsox9JegXWNaDrReVnaF2Di8xuaQ9LbauMue1amRZ4BIXx
+# ImNgAJo+t5+wNDc8RNs8nt9AMeNTms3OIv7GkoaHtVGcaLTJyrBIeWRemQPgGw1A
+# lNnYUNgr1+NMcwg+S4OQX28rDaVO/z/5Ozom9xzE68wZvgXkFPxF5ZAN0vTvNKiW
+# poDJZNQi0arlOXMgPKeUzD+4z0qlHmo3o6Rrf2N3AjbYZoPp8bLQyfLgjElPc7rB
+# aH5RVV4NRWBj7BEYxeRb+soDoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIHnJsbpJ
+# 9PrJdMrPrOks6tUApVJVpOn9x/KDwmZ2ttEiAhEAyukaE726WxrYoh6XvdYbPxgP
+# MjAyNTAyMjMyMzQ3NTRaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3NTRaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCDGnmN9b+RdcIlyLWijN4ZW8BmpsUGhVZ2jM3yh
+# 105sMTA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgCjOQvTWakLrS8dAl+dWPgV
+# G+sc6gEhTI3arM6HTH2FjMIY79XqVQVoORoUuskVFycV47VrTXQZ74UP3w1LVTYH
+# LAgerBNraIQ3Ndznul2pJlARMtMdSay4HRGTLVlPJdWOLixvTyDUUes1r7XOCDhd
+# y9AE+uVFkROU16DOGV5FJb4urmI9bO+/WZPBD9PXqsnwT47L8s+hOn04bi0DBEPi
+# mqt4xxnUqGM5GSD3UAGLP1VIpWs1HRKQsVqN4xOkUxZ3+7GjU4N+zQVKNgynw4Ya
+# KpP08DWCy+7CFvBBbm8ggdNd+UYq+42W2nL+oRu6Shew7thWlucKyAIIyfcfyEbD
+# NfW05chwbUJu0drAPMMJZFjYJMx0LHxRyDzFH3sbXjJpYMb4JczhDJ0KmIxvC3QV
+# Nz1ZDxo0DPVISKYZlAwXtlPXbN6uBmNnmUYnlJmRDCTgHYc6VwvXt/MZ16wdxMSB
+# 6kaa+L2tPgoo47NHkq51BxKpuIAkTXMJaIfrKUe40DFGbnCWXpw/yQHvLwxFNVjF
+# piUMl5ODIWFOZzj2ZZ/3juuslQoIieZZy2vdpAoklEHfHRstVgzr/8YGzzZEZ5dq
+# mcwRPigF+E93XsCqGkXRU9v2US/W4+v4xr5kSlAAXVS8mRdomfu8j0BsI2i7nJjN
+# hQP3P2swB3NCZCxgi1FOtw==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/ja/strings.psd1 b/PSAppDeplyToolkit/Strings/ja/strings.psd1
new file mode 100644
index 0000000..4618117
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/ja/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "完了です"
+ Error = "失敗。"
+ FastRetry = "未完了。"
+ RestartRequired = "完了。再起動が必要です。"
+ Start = "開始"
+ }
+ BlockExecution = @{
+ Message = "アプリケーションインストールが完了するまで、このアプリケーションの起動を一時的にブロックしています。"
+ }
+ ClosePrompt = @{
+ ButtonClose = "プログラムを強制終了"
+ ButtonContinue = "続行"
+ ButtonContinueTooltip = "上記に記載されているアプリケーションを終了してから「続ける」を選択してください。"
+ ButtonDefer = "後で"
+ CountdownMessage = "注意: これらのプログラムは自動的に閉じられます:"
+ Message = "インストールを実行するために、下記のプログラムを閉じる必要があります。`n`n実行中のアプリケーションを保存し、閉じてから続行してください。 または、実行中のアプリケーションを保存し、プログラムを強制終了ボタンをクリックしてくだい"
+ }
+ DeferPrompt = @{
+ Deadline = "デッドライン:"
+ ExpiryMessage = "再試行可能回数が0になるまでは、都合の良い時にインストール可能です。"
+ RemainingDeferrals = "再試行可能回数:"
+ WarningMessage = "再試行可能回数が0になった場合、システムで強制インストールをします。"
+ WelcomeMessage = "このアプリケーションはこれからインストールされます。"
+ }
+ DeploymentType = @{
+ Install = "インストール"
+ Repair = "修復"
+ Uninstall = "アンインストール"
+ }
+ DiskSpace = @{
+ Message = "ディスクの空き容量が不足しているため、インストールを完了できません:`n{0}`n`n必要な容量: {1}MB`n現在の空き容量: {2}MB`n`nインストールを実行するために、容量を確保してください"
+ }
+ Progress = @{
+ MessageInstall = "インストール中です。 少々お待ちください。"
+ MessageInstallDetail = "インストールが完了すると、このウィンドウは自動的に閉じます。"
+ MessageRepair = "修復中です。 少々お待ちください。"
+ MessageRepairDetail = "修復が完了すると、このウィンドウは自動的に閉じます。"
+ MessageUninstall = "アンインストール中です。 少々お待ちください。"
+ MessageUninstallDetail = "アンインストールが完了すると、このウィンドウは自動的に閉じます。"
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "最小 化"
+ ButtonRestartNow = "今すぐ再起動"
+ Message = "インストールを完了するために、再起動が必要です。"
+ MessageRestart = "カウントダウン後にコンピュータが再起動します。"
+ MessageTime = "実行中のアプリケーションを保存し、再起動してください。"
+ TimeRemaining = "残時間:"
+ Title = "再起動が必要です"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} は自動的に続きます:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - アプリ {0}'
+ DialogMessage = '次のアプリケーションは自動的に終了しますので、作業を続ける前に保存してください。'
+ DialogMessageNoProcesses = 'インストールを選択してインストールを続行してください。延期分が残っている場合は、インストールを延期することもできます。'
+ ButtonDeferRemaining = '残る'
+ ButtonLeftText = '延期'
+ ButtonRightText = 'アプリを閉じる&インストール'
+ ButtonRightTextNoProcesses = 'インストール'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDdacHd5r09xMOs
+# suu60DkVZZkKKiy8dHgIoWXWRDhlWqCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgLboCtusP3IflmMG4ThkY1XIisvj4bTgrJaC4rEfLuVww
+# DQYJKoZIhvcNAQEBBQAEggGAJIpwyw0XnZctLPAtxLBRptnIGu+KOaU1hU9pheE3
+# xDZzHJOUCyC6aNiJmhFu9H78Zn6WqYZJe9nk1cpLGXrWxHg1ObZtTka3fXhbE+/o
+# 3zBwV8VRyfrjnjefyynAkqmjEjHiFIb0dYOESiVVSp2exx4ABstmyNbuoP5dlSK7
+# cOo6Rc4lJuV0Ka7Mqd/o3nG4O3yhyHLtVjwqjeTZ1XzwXt4XVskRCTbKmyYjJbub
+# gnQu9YRBqG+SlQC6OOawAzbAZZAkIOc+dkG9iYNgeYMFRQCqixZ51UqC9GsBXH7p
+# gBG7tXYt5ABYBaNhHx2EPyFBrqkpSHi43/GR2xK3D/WgEixXcI7zp6RX1NfYtPvf
+# 0Im9X7apL3UwEFVMljBqvy0cyUS2fC4nCE90tr8toSmqb6bDOE4x/isusSTgDcg8
+# tWOmhDci3Rpk4ExohYy+UjLQ10QD7aj8duIWGOBRRs1zyKiSkQBf0j0XODzzTXrX
+# +tAUyaEqvFpooBCU8QaJfAWWoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIE3AIDnF
+# G+O6PIHjuvQvReaevWb3bmODRwOaDqop4ghmAhEAuQ3CGCrgiA5OQwOyT+PPQhgP
+# MjAyNTAyMjMyMzQ3NTZaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3NTZaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCBo1CwuqbP9xdOpWydiZGhY/ZWCOJjbIWqOIZ5S
+# 5D7j2DA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgAqgFdBAJGZb4FGvJHe5Be/
+# +d1xQ/HlqG5FEDQy9Ie6OG2Yc1VrCuTZNUO449TKqou7dVyTmLMMLY8lG5rg6P91
+# fPpJlN23UKwcmDJ/akyuTc4svfhpriUO/DqlYtUTHm4Uk1RN+9HdZHf0EnhOEnAE
+# 8rDnQzqT/UKeGcCWY23XiQ8oFvifTB9yQ55QOW+Q6yjOOMdauEu4xu2tW2L818iA
+# tycGVHshEhxn98zUZxxSOKylTPqfXq7qYNC3vHE5WyNMA4BUGNbKoDN/eYZcDqv9
+# K0awKx6rNB71XHHvsRILBLWGnL4obGebn/X0e1Cnwj2v8rXhCGMNiuLKlcd0Ccz1
+# BzoBeh/EiWGPTSggrF0g/+T5fXABzZ1fMGqnSRKyb0a/9L8B22LnXaFi1kQlEnsb
+# kTI+0e8jWpp1BH7quLnOIgH2382LCY8UIKCfJCx3gP2f+FMq79RZOQItbdEIQ+Vx
+# H42ue4NMzhZg1Ohtz2GjSk+VePO1Ew8vknMs4Qt2taC4wwn9F684kujfJrKat6y2
+# o3nEPWNvttGXUP4KKf3LfI5/WlpkrL0oK125vx+M+ffrCYw/g1RXF7YxVEYgdxD9
+# rcnahy4mPgLlvX6RGzsHE+IIy2IFMtUxecaxxnIL4wLiKAi47OEL114V5Vak7t46
+# v0JlMW4EfU3nz5fMrNnHyw==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/ko/strings.psd1 b/PSAppDeplyToolkit/Strings/ko/strings.psd1
new file mode 100644
index 0000000..6d2f162
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/ko/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "완료되었습니다."
+ Error = "실패했습니다."
+ FastRetry = "완료되지 않았습니다."
+ RestartRequired = "완료되었습니다. 재부팅이 필요합니다."
+ Start = "시작되었습니다."
+ }
+ BlockExecution = @{
+ Message = "설치 작업을 완료할 수 있도록 응용 프로그램의 시작을 잠시 차단했습니다."
+ }
+ ClosePrompt = @{
+ ButtonClose = "프로그램 종료"
+ ButtonContinue = "계속"
+ ButtonContinueTooltip = "위에 표시된 응용 프로그램을 종료한 후에만 `"계속`"을 선택하세요."
+ ButtonDefer = "연기"
+ CountdownMessage = "참고: 프로그램이 자동으로 종료되는 경우:"
+ Message = "설치를 계속하려면 다음의 프로그램을 종료해야 합니다.`n`n사용자 작업을 저장하고 프로그램을 종료한 후 계속하세요. 다른 방법으로는 사용자 작업을 저장하고 `"프로그램 종료`"를 클릭하세요."
+ }
+ DeferPrompt = @{
+ Deadline = "마감:"
+ ExpiryMessage = "지연 기간이 만료될 때까지 설치를 연기할 수 있습니다:"
+ RemainingDeferrals = "남은 지연 기간:"
+ WarningMessage = "일단 지연 기간이 만료되면 더 이상 연기할 수 있는 옵션은 없습니다."
+ WelcomeMessage = "다음의 응용 프로그램을 설치합니다:"
+ }
+ DeploymentType = @{
+ Install = "설치"
+ Repair = "수리"
+ Uninstall = "제거"
+ }
+ DiskSpace = @{
+ Message = "다음의 설치 완료를 위해 필요한 디스크 공간이 충분하지 않습니다:`n{0}`n`n필요한 공간: {1}MB`n사용 가능한 공간: {2}MB`n`n설치를 계속하려면 디스크 공간을 충분하게 확보하세요."
+ }
+ Progress = @{
+ MessageInstall = "설치 중입니다. 기다리세요..."
+ MessageInstallDetail = "이 창은 설치가 완료되면 자동으로 닫힙니다."
+ MessageRepair = "수리 중입니다. 기다리세요..."
+ MessageRepairDetail = "이 창은 수리가 완료되면 자동으로 닫힙니다."
+ MessageUninstall = "제거 중입니다. 기다리세요..."
+ MessageUninstallDetail = "이 창은 제거가 완료되면 자동으로 닫힙니다."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "최소화"
+ ButtonRestartNow = "지금 다시 시작"
+ Message = "설치를 완료하려면 컴퓨터를 다시 시작해야 합니다."
+ MessageRestart = "카운트다운이 종료되면 컴퓨터는 자동으로 다시 시작합니다."
+ MessageTime = "사용자 작업을 저장하고 지정된 시간 이내에 다시 시작하세요."
+ TimeRemaining = "남은 시간:"
+ Title = "다시 시작해야 합니다"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0}는 자동으로 계속됩니다:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - 앱 {0}'
+ DialogMessage = '다음 애플리케이션은 자동으로 종료되므로 계속하기 전에 작업을 저장해 주세요.'
+ DialogMessageNoProcesses = '설치를 계속하려면 설치를 선택하세요. 연기할 항목이 남아 있는 경우 설치를 연기하도록 선택할 수도 있습니다.'
+ ButtonDeferRemaining = '남아있음'
+ ButtonLeftText = '연기하다'
+ ButtonRightText = '앱 닫기 및 설치'
+ ButtonRightTextNoProcesses = '설치'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBG5DGocAB99UxI
+# g6MflR9wStK5yt3VPzy6DwrkSFvbGqCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgRw5rIkXdqm6QGu8HKRSJzUBnbqeRD7DDpiNpCXWeuIIw
+# DQYJKoZIhvcNAQEBBQAEggGAXZSuiKYUcHGZK5ppYfnetWYz/qLSUXZwbxjs4xbQ
+# ZM/a+Cc0uEtRzpGpUxApyOMh7v63JJOTrtpSKRDrNKzRAd1l7+qKxpngqQ/C0JJ8
+# hNVMsHCT4FP8rHtgEsfb4MUCVhwk/pFhH8QKwooxazUO1YrDeO/gAZohSJ+fvYpo
+# gr010SZNNOSe+uhDOP5rnypIH33oKCUtlw0hkrYT9kt3KZcFVfu+yeecKRjDLVyl
+# wR62mJi2OMu8mkauHY9EH/9RRXyX+F/okOGIb9FHWAGzgwA0u2WYrChNUcDAdsCN
+# cU5ph1CF6f1w7/grccqMosNXcpTQpKWhUDeprR+A/jZ7UjNg1Cnm/pqQ5NLVrAjo
+# CohxaZeo4yymqDUHehA9Lb3lRaKGMRidcWrtRHKHracLPnSdTYDtsKgM8Vfcfgir
+# dHPv4C9c8LZPCgY4bVllWmhh5lgVgapLI6PVHamqkEkSKBKIEkXu3T0Z4IwnzpSF
+# lE29TSaO9XNwWDyGs3wKsPwfoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIPrGmooi
+# omdxKtxoj0E37LTtWSfrRvrN6bAURCTV3GRYAhEAsa09OrdKS8tnvhvX8t8nUxgP
+# MjAyNTAyMjMyMzQ3NTlaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ3NTlaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCCUJ6fOUijX5UzKvnegK0WN1qwx98wG7lGN8PZ4
+# ZYPdJDA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgARaxzHf3CuTws00bozL6Ze
+# 6p79NT0nSsGfFAX8thqyjaGuQWvpndbYzyikDUosr2D62MPmvyqhN2Dbu6kzmgcW
+# J/Fn0xqJNZdZs9x6CjdQcY4NU0OEN7BlqfM+fDj4L55KE2U9xXJxzROnjxwDHIuq
+# 3BA4jyH6b3vQYJYQXf+ypSZ/BIo24AyxD4Dweyfsx/VyApdEMoVcD/oEiFvhJCEt
+# HdKrQ3+e8HtovNKtSaAQAgIuMMh6riEHRicc2P66PNgdj9IFLALjfMmiNFMwmGZ4
+# wQeA2kwne2fIGLz6QVKXw9ycMT5JpJ2CWr3de6jKUjeWQCIWJ5FX6PYZ14adbPyv
+# VUt3cBwFQ5ODrtinewCS+ec7fNGNBvvHZUkcnLxET5DOj1e+qmBk3WUCdlj5gjm3
+# YFpkdrroccoijkJ9RbyGM9ZvZdxlK0pwVlQUUHYUWlF4k2dRNdhNEs3GuhrCAV3A
+# a/TWrzeqbFl6aSOx1mNugBDCnRAnsMp4zkTehTCokda7uUIfrNAtekNyoQeLBgn1
+# eEnCc1RmwD6W4qivKtKQha99G2ppwg4LsWb8namMBf2AOWI7kMjDTh+BE9lzNKj3
+# cNX5937k+7JJysvCodZfy4IXl/hP6ddPA0u4IGA+KWEzJcZOR3tq7Vp7Pb6AWeTf
+# Yj6eX6527jVv7AIWYmqWKQ==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/nb/strings.psd1 b/PSAppDeplyToolkit/Strings/nb/strings.psd1
new file mode 100644
index 0000000..49bb9ca
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/nb/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "fullført."
+ Error = "mislyktes."
+ FastRetry = "ikke fullført."
+ RestartRequired = "ferdig. En omstart er nødvendig."
+ Start = "startet."
+ }
+ BlockExecution = @{
+ Message = "Start av dette programmet er midlertidig blokkert inntil pågående programvareinstallasjon er fullført."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Lukk programmer"
+ ButtonContinue = "Fortsett"
+ ButtonContinueTooltip = "Velg kun `"Fortsett`" etter du har lukket applikasjonen(e) i listen over."
+ ButtonDefer = "Utsett"
+ CountdownMessage = "OBS: Programmet vil automatisk lukkes om:"
+ Message = "Følgende programmer må lukkes før installasjonen kan fortsette.`n`nLagre arbeidet, lukk programmene og velg `"Fortsett`", eller velg `"Lukk programmer`" uten å lagre arbeidet."
+ }
+ DeferPrompt = @{
+ Deadline = "Frist:"
+ ExpiryMessage = "Du kan velge å utsette installasjonen et begrenset antall ganger inntil fristen utløper:"
+ RemainingDeferrals = "Gjenstående utsettelser:"
+ WarningMessage = "Når fristen har utløpt kan du ikke lenger utsette installasjonen."
+ WelcomeMessage = "Følgende program vil bli installert:"
+ }
+ DeploymentType = @{
+ Install = "Installasjon"
+ Repair = "Reparasjon"
+ Uninstall = "Avinstallasjon"
+ }
+ DiskSpace = @{
+ Message = "Du har ikke nok diskplass for å fullføre installasjonen av:`n{0}`n`nLedig plass påkrevd: {1}MB`nLedig plass tilgjengelig: {2}MB`n`nFrigjør diskplass for å fortsette installasjonen."
+ }
+ Progress = @{
+ MessageInstall = "Installasjon av programvare pågår. Vennligst vent.."
+ MessageInstallDetail = "Dette vinduet lukkes automatisk når installasjonen er fullført."
+ MessageRepair = "Reparasjon av programvare pågår. Vennligst vent.."
+ MessageRepairDetail = "Dette vinduet lukkes automatisk når reparasjonen er fullført."
+ MessageUninstall = "Avinstallasjon av programvare pågår. Vennligst vent.."
+ MessageUninstallDetail = "Dette vinduet lukkes automatisk når avinstallasjonen er fullført."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimere"
+ ButtonRestartNow = "Omstart nå"
+ Message = "En omstart av maskinen er nødvendig for å fullføre installasjonen."
+ MessageRestart = "Maskinen vil automatisk starte på nytt, når nedtellingen er omme."
+ MessageTime = "Lagre arbeidet ditt og ta en omstart av maskinen innen fristen."
+ TimeRemaining = "Tid som gjenstår:"
+ Title = "Omstart kreves"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} vil automatisk fortsette om:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - App {0}'
+ DialogMessage = 'Lagre arbeidet ditt før du fortsetter, fordi de følgende programmene blir lukket automatisk.'
+ DialogMessageNoProcesses = 'Velg Installere for å fortsette med installasjonen. Hvis du har noen utsettelser igjen, kan du også velge å utsette installasjonen.'
+ ButtonDeferRemaining = 'gjenstår'
+ ButtonLeftText = 'Utsette'
+ ButtonRightText = 'Lukk apper og installer'
+ ButtonRightTextNoProcesses = 'Installere'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA7KAyXVqY6EmiX
+# c+s/Py5AV02CNgI4rFrEiOh7L93BjaCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQg4Oj5PZuqNY7yz444sCuD/DthJH8vEKuNgBxAVVtyNIgw
+# DQYJKoZIhvcNAQEBBQAEggGAmX+JuZoEP1sirRt6T5is2AQV6IHMNzxzDtpSHrJy
+# UIyZCZ8MlCpAdQj7KLQuMZzU8U0fzko07S0vhsrocyOlb00XdSPsjyDbtUXhL6Bx
+# hlK08UIJfne5Ic9UWTB7YYhKBavGFD7q+G7bmBy2nFKVI6zsQbcuJQ23PYbzRamt
+# YhaPDOSYRJ3ZcL6jxhGOZ6x1bH5OfZrlXFALTrZOsT0YIrMlgzTRX+PYiyuyeD05
+# xg8TG1LFctwJ19K6GkPrh/C0LVyDhYOY/cywW9qzy6NExUGaxdIV787JXNKYY4Y2
+# mtF4oy9mpRvoFfOZd0gI5TPyn+hNsC5t5j1znju29SDAkHLPM7yyhFHjsQ0V68X7
+# W1ai+rrMdn5KR23qCgEit1oOdjYkW4iXfhCLSApMWCkVi/s+rl9mmB6pPnT/w6le
+# g/iTGv0QNkmQqBt3wYLOKiEyMAa9WXImn5amVd3xi9AYIENuCZ8GGL5eySjFQf20
+# wjYhSafmy7L6afhcAqvHRGwKoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIAn7RF/4
+# BbfG9XHj+AbhxXIcWjyIZNzT7U7ptkqfrwUfAhEArMbCnFUZIM73XLM1GxgCmhgP
+# MjAyNTAyMjMyMzQ4MDJaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ4MDJaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCBDV/lAOf5AsR+CumCpzzOqBT42ThFmCER+c9zC
+# kEUzgjA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgCifeaK1x3UpQnxzEVRvHvL
+# ja2gvf03eXVr9HSRLZh0IxendmowMlxCdHr266luyrbFuAJ4XAQtBnS1eOFa/DkQ
+# Aaq+lqLAAXP8v1egRdlTk8aCDlgaECncRQJHubAxt7wauK4NWEZCy343dFJoBHVU
+# TmA0sTkVNkEp+9juNUfNRzbf9n438aWpsHN1aWGdY8eU0J+/C/TIa0DkCDAQ0Hu3
+# Aw7zFqnlVyqO5mRT1A2FZL4TR6RrH5PUJFMBcnvCf+1UPK9ODx2rNnBDU39aOm5B
+# Yu8TRpoD5m+izi7+Sf9To9UXv83A9kOTw1+JGx15XF+gQ5tGHu6fvPxt+sEd2rwK
+# cz44+vJDjzt4fUqigReStryK7c3kQPcxFJeqM5P19egnaO5Pii0Vk/OGBxwbERhx
+# Js2UUsO+KME5qdamzIsNWhziuNjoxUIC1Q6zxjxONBWc0qccKP3nxP1Czv9OPglC
+# pRU6LgjGgie3erB1CQzB524NyjcqYLnbxWhA2fCuLqu5VhlxpUTmDpzoy0iCcIAR
+# QNN4spbsAsboie6bQenAfYTQ5Gs4d44esgU+ujUNXZlCEuKDHQD8vMOwWN2a6QFE
+# eomezdssDhqNFxjqA669ivfihBsfJ2uZDkmJq8vhMKJW5U9ndDOnIZiFvAH83dym
+# 9WpAKCzeYjfEBRV53X+pHQ==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/nl/strings.psd1 b/PSAppDeplyToolkit/Strings/nl/strings.psd1
new file mode 100644
index 0000000..4f2dade
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/nl/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "voltooid"
+ Error = "gefaald"
+ FastRetry = "onvolledig"
+ RestartRequired = "voltooid. Een herstart is nodig"
+ Start = "gestart"
+ }
+ BlockExecution = @{
+ Message = "Het opstarten van deze applicatie werd tijdelijk geblokkeerd om een installatie uit te voeren."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Sluit Applicaties"
+ ButtonContinue = "Doorgaan"
+ ButtonContinueTooltip = "Selecteer alleen 'Doorgaan' na het sluiten van de bovenstaande toepassing(en)."
+ ButtonDefer = "Uitstel"
+ CountdownMessage = "LET OP: De applicatie(s) worden afgesloten over:"
+ Message = "De volgende applicaties moeten afgesloten worden om de installatie te voltooien.`n`nSla je werk op, sluit de applicaties, en ga verder.`nOf, sla je werk op en klik op 'Sluit Applicaties'."
+ }
+ DeferPrompt = @{
+ Deadline = "Deadline:"
+ ExpiryMessage = "Je kan de installatie uitstellen tot de maximale uitsteltermijn is verstreken:"
+ RemainingDeferrals = "Aantal keer uitstellen:"
+ WarningMessage = "Na verstrijken van de uitsteltermijn is deze optie niet langer beschikbaar."
+ WelcomeMessage = "De volgende applicatie wordt zometeen geïnstalleerd:"
+ }
+ DeploymentType = @{
+ Install = "Installatie"
+ Repair = "Reparatie"
+ Uninstall = "Verwijderen"
+ }
+ DiskSpace = @{
+ Message = "Er is onvoldoende schijfruimte voor de installatie van:`n{0}`n`nRuimte nodig: {1}MB`nRuimte beschikbaar: {2}MB`n`nGelieve voldoende schijfruimte vrij te maken om de installatie te starten."
+ }
+ Progress = @{
+ MessageInstall = "Installatie bezig. Even geduld..."
+ MessageInstallDetail = "Dit venster wordt automatisch gesloten wanneer de installatie voltooid is."
+ MessageRepair = "Reparatie bezig. Even geduld..."
+ MessageRepairDetail = "Dit venster sluit automatisch wanneer de reparatie is voltooid."
+ MessageUninstall = "Verwijderen bezig. Even geduld..."
+ MessageUninstallDetail = "Dit venster wordt automatisch gesloten als de de-installatie voltooid is."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimaliseren"
+ ButtonRestartNow = "Herstart Nu"
+ Message = "Om de installatie te voltooien is een herstart nodig."
+ MessageRestart = "De computer zal herstarten als de teller op nul staat"
+ MessageTime = "Gelieve je werk op te slaan en binnen de toegestane termijn de computer herstarten"
+ TimeRemaining = "Resterende tijd:"
+ Title = "Herstart nodig"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "De {0} gaat automatisch door over:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - App {0}'
+ DialogMessage = "Sla je werk op voordat je verdergaat, want de volgende programma's worden automatisch afgesloten."
+ DialogMessageNoProcesses = 'Selecteer Installeren om door te gaan met de installatie. Als je nog uitstel hebt, kun je er ook voor kiezen om de installatie uit te stellen.'
+ ButtonDeferRemaining = 'resterend'
+ ButtonLeftText = 'Uitstellen'
+ ButtonRightText = 'Apps sluiten en installeren'
+ ButtonRightTextNoProcesses = 'Installeren'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC89V079WaT7dz4
+# CrBoFvn55vEKcqkGIXeImtwIU10FPqCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgz0PNPZReYQaI15aYPpoEJzDhY8kBc57PdZV655nW9Usw
+# DQYJKoZIhvcNAQEBBQAEggGAlc0BrdtdDZ97dfTdwKtlOcwIp7pqa2jCye8+xgma
+# zb1dsyCgL+6dvezDr+xygkZRbttWzf0GMVrpXwM9ijoPneSA1owApiuNxv5LBakx
+# t/hQEAKUWqnkzMSYVpi8+VgcX58kmQ6LTP7Bag9KL5CbrCx4m8bqv8hh4NGtRPXN
+# l4psSUQGj/eKWRvpymue6i3/0Yi9ObSi4ZITO8unKy/BK4ao/WOuTo027kEWeB3W
+# F6+xV8mAAiU64Z08r8ixl16aeUicIr45b8V1ZmfAzpgC5RbDEG7IvPyOcLHWQl+L
+# 4SALurJk5gHQLRYjsZPfZW6Bx8R/ZbJk9qPBeRY8A/5LcCwV78DfAMYTaSMtUOeU
+# ICe/EWuihcBNNYu/zS5jl7fsJSk8mxgMQ/xdxVFDyKCAtD7YubsS3BfEOGL9Vp4Q
+# JsqtxI9IDnI9u1lL44AY9Osx86mc1VWQXhn7jB5Xzwi8yihGImg9AxlKaBbozEVw
+# 3l0Xp90ZPoP4JntErwyruS1ZoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIJpP4yXm
+# 0YWj8/HUWkoljQwndq2cgltlOqm8P/YMnOVwAhA/MtVDciYwpqbYUj5wor9bGA8y
+# MDI1MDIyMzIzNDgwNVqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgwNVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIIfuuurmAskiHjDr4acxkefcMdp+aquwfj4VYOHX
+# J3FtMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAKDW3YDIGY2ujYd26TWySiu5
+# MwZtCsjt1zFAHCk0F8s2CbUKVhQmK75FxYraDcBau1xkDU7H8Ss101mjevCtvXOp
+# x9u5SeO6vJDLDdHEEKYwVAe4n69tlvMJxX6xwxf1oEK/dKSdEu9SCMdP2A7zqBqX
+# 8c7hyqnisD84rk2cBeey7HlY0WBeoMHCwM/N9WB3Q0VBzgli01V+zhlXYjjaQbr0
+# m2t5KflIv40/WzzC1oLG/cvpr86a8ttvH11TEQesc6OlPCTPmwvSfLOjhtv7YM6Q
+# 4C7z5a4QMsPk/kTYkAR34B2THhKDFyAomeigP6em4ZP8qctGSmBtnY09ek9CVZmb
+# DWOW7DuKUCUyHabOYxOQ9iqZ4LJnJLNtIO1q5rVqDWDgwC48kkh6249fNy0QDnjN
+# OBYVvwPPUecMpwgFR8HpiWSSMwI4kVVb6YwdUlmP4aUjyrkuXY4ALLfXebjpeQUc
+# 1bRK4ypTr/XF5aIemYDZXTVFcqEh/5ErxSJgqnkBirp1ztpfRAXrLDtESPZhCMkr
+# dpg8ii93XXyV6qQczNAwBXrapvAL9zfENeaVyLuYWM0t4Q2NIlE5zfSI3Wh0TAya
+# 7CdknbO/dA48qjyj1tswfEKFE5rX8nBddTbtRLjvMouFtnwhdCi0yLA3HT/7AJah
+# MLaajfp4NgHGk4uYzt3d
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/pl/strings.psd1 b/PSAppDeplyToolkit/Strings/pl/strings.psd1
new file mode 100644
index 0000000..831d66f
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/pl/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "zakończona."
+ Error = "nie powiodła się."
+ FastRetry = "nieukończona."
+ RestartRequired = "zakończona. Wymagany jest restart komputera."
+ Start = "rozpoczęta."
+ }
+ BlockExecution = @{
+ Message = "Uruchomienie tej aplikacji zostało zablokowane na okres instalacji."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Zamknij Programy"
+ ButtonContinue = "Kontynuuj"
+ ButtonContinueTooltip = "Tylko wybrać `"Kontynuuj`" po zamknięciu wyżej wymienione aplikacje."
+ ButtonDefer = "Odłóż"
+ CountdownMessage = "UWAGA: Programy zostaną automatycznie zamknięte za:"
+ Message = "Następujące programy muszą zostać zamknięte przed rozpoczęciem instalacji.`n`nProszę zapisać wszystkie dokumenty i zamknąć programy, a następnie kliknąć przycisk `"Kontynuuj`". Alternatywnie zapisz wszystkie dokumenty i kliknij przycisk `"Zamknij Programy`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Ostateczny termin instalacji:"
+ ExpiryMessage = "Instalacja może zostać przełożona na późniejszy termin."
+ RemainingDeferrals = "Pozostała ilość przełożeń instalacji:"
+ WarningMessage = "Jeżeli zostanie przekroczona możliwa ilość przełożeń, opcja `"Odłóż`" będzie niedostępna."
+ WelcomeMessage = "Zostanie zainstalowana następująca aplikacja:"
+ }
+ DeploymentType = @{
+ Install = "Instalacja"
+ Repair = "Naprawa"
+ Uninstall = "Deinstalacja"
+ }
+ DiskSpace = @{
+ Message = "Brak miejsca na dysku:`n{0}`n`nPotrzeba: {1}MB`nObecnie wolnego miejsca: {2}MB`n`nProszę zwiększyć ilość miejsca usuwając zbędne pliki."
+ }
+ Progress = @{
+ MessageInstall = "Trwa instalacja. Proszę czekać..."
+ MessageInstallDetail = "Okno to zamknie się automatycznie po zakończeniu instalacji."
+ MessageRepair = "Trwa naprawa. Proszę czekać..."
+ MessageRepairDetail = "Okno to zamknie się automatycznie po zakończeniu naprawy."
+ MessageUninstall = "Trwa deinstalacja. Proszę czekać..."
+ MessageUninstallDetail = "Okno to zamknie się automatycznie po zakończeniu dezinstalacji."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Zminimalizować"
+ ButtonRestartNow = "Restartuj Teraz"
+ Message = "Aby instalacja została poprawnie ukończona wymagany jest restart komputera."
+ MessageRestart = "Komputer zostanie automatycznie zrestartowany po upływie wyznaczonego czasu."
+ MessageTime = "Proszę zapisać wszystkie dokumenty i zrestartować komputer w wyznaczonym czasie."
+ TimeRemaining = "Pozostały czas do restartu automatycznego:"
+ Title = "Wymagany Restart"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} będzie automatycznie kontynuować w:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - aplikacja {0}'
+ DialogMessage = 'Zapisz swoją pracę przed kontynuowaniem, ponieważ następujące aplikacje zostaną automatycznie zamknięte.'
+ DialogMessageNoProcesses = 'Wybierz opcję Zainstaluj, aby kontynuować instalację. Jeśli pozostały jakieś odroczenia, możesz również opóźnić instalację.'
+ ButtonDeferRemaining = 'pozostać'
+ ButtonLeftText = 'Odroczenie'
+ ButtonRightText = 'Zamknij aplikacje i zainstaluj'
+ ButtonRightTextNoProcesses = 'Instalacja'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAZOoKrB2rAL4dP
+# 9wFxGI5xBPC7bA/epQPo90KCV5Jd8aCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgUu2mwnjscA96epC46AgI5ThjsWNtuhDdWTsCDT7QNbMw
+# DQYJKoZIhvcNAQEBBQAEggGAtmTkOMTmw1iseylplieD6Lnhs07WmHqm6jnvzxro
+# 73aWwD/XknSgOqEu0duzrUXh0GRKeMF9xrsXxKIFtvv/mdy5hxGMiR3yWeu+peS8
+# U1Ro3E5AVKD0JblUc0WvyNmnfrtacBO5RWoGlgVPdrU+pKdL4E+yjuWXOhwqdZ1g
+# 21AglCdWiYCkH9Q486Z4hafd+oZtOZHn5Pvlak2QuQAt9UHpcCCPX+sBtafp3b1q
+# OMm17LClB9aGvehlWoVSGE6VocW5A1h3uxYkM5D00pFDqcS3XkgG1qaG/uJgEhbl
+# w7/nqGaTSJloHAHAGXDWcCejahJDYdcKopMIeR5vMtAbMY37sHEtSA2CVXuFw1/Y
+# AGe+Ig79LxMwo9SgsIoR7tpvGps2bq/wyE0aLUEug89ZJkETN48zVQ8n2wEnEc08
+# m95vUUIoHVhfZ4U2cMHhRc/o7g+OIzADVxN1yX/cF7pvtgLvNwKTJj3159uRjacm
+# 8kVvq/De8ER/Tn1qqChA8v7ioYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEINHvkpvo
+# C2ROHIwrxpJ9MkSm9dptNw0AdxHZjq2Y9R0MAhA27sgRxJbPsPwDPd5KSiSdGA8y
+# MDI1MDIyMzIzNDgwOFqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgwOFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEINMk/JF+pXLToSzD7RruPbdOqEztwRsfb5VweHv5
+# cZ+AMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAAQEqEPrgFNwZBRwyePFvRwn
+# dOB5CkDKjI90YYCeJeilS7esfs23ek7bvxJWINMTlJKJ8dWQB0Z7tGlsztJBRyqV
+# Wx4+nnquB6J14HQ+jdrIQT/+K63++qb0nm+zNeoieHrpWxNPsNhOozVlnDVs6lev
+# EPG8JNaBbFaeYudJi3LDr1bH/SDmK8cHsPxeNkSXBdWcMOO86KDud4HP1qK+baAF
+# PM1Iu9oNxzqT8VjvWFa8fdko14U5FpbJAVcGOShlHCGxzKnjW5IKWDP7rQxAXXUx
+# oevsnGnqFEL1yx3KVPzFzU5wkxHEJcfDt03XAKcrg3P6AX0gJjUTRAswrfFDnFIe
+# tGUpOJY9ej5nPck2XqZpXcnjuz2Vp9P4T2gzWcvA3q+5H3Uc7ILp5uNurar7NmhI
+# fj9PLz8J5ZNkTasEvLLiycAr0WSu+jIFacWYndOmN1P4W+IPutU7itE+8h7A8IDM
+# axybBOngxJ21la6TQBWWw46koonftk6eSepebTls5C5ObIj1bwCqR5IuZjgxGxkl
+# RR3vG3OQ3Tkis35YEibK9rnZ2YxJ5sZoYOpmGSp8WrWeOHx4X3hX5hgaSvPV9bdO
+# BH6XW0LR6BsaY/rnLphR09ks6PUrWSPDlnTVKekCt0NEMiAWGLUfWMlyro6V1Fus
+# vVGGIglrSjFMIxRcilcU
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/pt-br/strings.psd1 b/PSAppDeplyToolkit/Strings/pt-br/strings.psd1
new file mode 100644
index 0000000..c908680
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/pt-br/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "concluída."
+ Error = "falhou."
+ FastRetry = "não concluída."
+ RestartRequired = "concluída. É necessário reiniciar."
+ Start = "iniciada."
+ }
+ BlockExecution = @{
+ Message = "A execução deste aplicativo foi temporariamente bloqueada para que uma operação de instalação seja concluída."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Fechar Programas"
+ ButtonContinue = "Continuar"
+ ButtonContinueTooltip = "Apenas selecione `"Continuar`" depois de fechar aplicativo(s) acima."
+ ButtonDefer = "Adiar"
+ CountdownMessage = "OBSERVAÇÃO: O(s) programa(s) será(ão) automaticamente fechado(s) em:"
+ Message = "Os seguintes programas precisam ser fechados antes que a instalação possa prosseguir.`nSalve seu trabalho, feche os programas e depois continue. Como alternativa, salve seu trabalho e clique em `"Fechar Programas`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Prazo:"
+ ExpiryMessage = "Você pode optar por adiar a instalação até que o adiamento expire:"
+ RemainingDeferrals = "Adiamentos Restantes:"
+ WarningMessage = "Depois que o adiamento expirar, você não terá mais a opção de adiar."
+ WelcomeMessage = "O seguinte aplicativo está prestes a ser instalado:"
+ }
+ DeploymentType = @{
+ Install = "Instalação"
+ Repair = "Reparação"
+ Uninstall = "Desinstalação"
+ }
+ DiskSpace = @{
+ Message = "Você não tem espaço em disco suficiente para concluir a instalação de:`n{0}`n`nEspaço necessário: {1}MB`nEspaço disponível: {2}MB`n`nLibere espaço em disco suficiente para prosseguir com a instalação."
+ }
+ Progress = @{
+ MessageInstall = "Instalação em andamento. Aguarde..."
+ MessageInstallDetail = "Essa janela será fechada automaticamente quando a instalação for concluída."
+ MessageRepair = "Reparação em andamento. Aguarde..."
+ MessageRepairDetail = "Essa janela será fechada automaticamente quando o reparo for concluído."
+ MessageUninstall = "Desinstalação em andamento. Aguarde..."
+ MessageUninstallDetail = "Essa janela será fechada automaticamente quando a desinstalação for concluída."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimizar"
+ ButtonRestartNow = "Reiniciar Agora"
+ Message = "Para que a instalação seja concluída, é necessário reiniciar o computador."
+ MessageRestart = "Seu computador será reiniciado automaticamente no final da contagem regressiva."
+ MessageTime = "Salve seu trabalho e reinicie dentro do prazo estipulado."
+ TimeRemaining = "Tempo restante:"
+ Title = "Reinicialização Necessária"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "O {0} continuará automaticamente em:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Aplicativo {0}'
+ DialogMessage = 'Salve seu trabalho antes de continuar, pois os aplicativos a seguir serão fechados automaticamente.'
+ DialogMessageNoProcesses = 'Selecione Install para continuar com a instalação. Se houver algum adiamento restante, você também poderá optar por adiar a instalação.'
+ ButtonDeferRemaining = 'permanecer'
+ ButtonLeftText = 'Adiar'
+ ButtonRightText = 'Fechar aplicativos e instalar'
+ ButtonRightTextNoProcesses = 'Instalar'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDqYT8oblxgA2vc
+# kkLVLK9Bf3aJtc2eVSs8bymMCha0BKCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgDexieKy2qA6VrJGGG51DUDHJiWhDzBYzQsTIJWti97Ew
+# DQYJKoZIhvcNAQEBBQAEggGAGnPHYpdJ/jl+GPoaT9bkDkseRwQAHGraOfUmHnvo
+# neMSD5LVoWJv00PtPSC5GyG1ara/qfkaZ022AXEzgGgMv0QoeZZduDrbNQo5mHXi
+# rh8KXdXO0SEeK2ZrHdYfdWY14I4vQCvvAS91pZFCcE67VdNnB744C8akbDDTgkwO
+# IHR7O+UEFbjHlvIFS7Moy512BH8xBLmtvjaj76HctM4/W531Qd/lg3W5G6p5SJ+t
+# 26yqH9UXQeghQkbPI2awcRIy4bGbkDVqViRWhnEd83SVDOygFCbvEUjIPYdjmIfC
+# 28gsqtPgn+DHYRWbBoYbc7n5fLLwqBGbnEJvje0bxGi1YpCiJCQ1D4BgcI+eocie
+# QQRt2TcqKb85J5WDxSHh+JUe0EkNFBUxlfJrYQoCXiuhLY7+0Cq7FqytDNfhBmra
+# /cqcbQ7YaNVHirp0w+7XIcJgVRx6pUEO3ap4jI0JxdXvtE9i5QaQm7fIgVR91F7z
+# cyo2e8rxvF7JIrfH6FYASm35oYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIDj94I7h
+# FpsATbCxe1pmpSfjZghHIWmIKTvKT1/r72otAhAiEtbYpgC9mSlgkWvhrhx4GA8y
+# MDI1MDIyMzIzNDgxM1qgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgxM1owKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIIKouk95szyct+P8hwpGOgnoXKGuZE8F7KhHmguq
+# MDrGMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAHIPH+SVGIT4W9QVLgiI3fqE
+# ICwupCavSvc5U6QKNm44/7/MaMhh0HgGv6I2DZqmNn9YcW1rZ+nkLWM+SToQbyom
+# gewOpzKui4QNW9ccSatrvDUmMhn0Y29nz4/WQ0cEbB37MKiFYPOmdUE+t0Tvf5V6
+# EGWtgiOaxeHdUl3DPfdWLee+ZlHaHD0HsJfsFmO0d9tBBeqHKajSbbH8aZLu+McT
+# slUfRfBzB0GcUO0utzW6HOKyItaKSt0KhlyBwzOegVRh4cfR72NaqJp82fRMfXVO
+# vpZvKN4WDRlK6dDrLcjhdg37+Y5QU8p6SzPQvPYHN5w8E+RCrh2Du/3BH0B6Lehm
+# OWoZ8cd7PM16dCyYr/BEKzr/kznmufbHEjEK3Viehg4F9Fxiw60NMLKG+MpVPCYp
+# DWmuH+WXAqj0G+l+gpqYunCpO2sofIrkAdVEgKAu9Y32TW6mWW6HSHgwsxBCETOu
+# wve9+jw0e3BlR2iP4FS2j58OiyVKA3WDaNBknSFc7w1FhB14o8+EnOtcx3CQIWBt
+# 8mEAGveLyya6FY6/kJpJWCLkaszb4RR/oqNGiPxivJVdozqk6A9Th3NxHq5ZI+3W
+# txoIcL9VlItCbBRoscMjbF35khrJaO4Cg34TlImOyRYzE1mji+UUIb3JepBpceLi
+# /asuLrUghsd5VYmUlmCZ
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/pt/strings.psd1 b/PSAppDeplyToolkit/Strings/pt/strings.psd1
new file mode 100644
index 0000000..0c20774
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/pt/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "completo."
+ Error = "falhou."
+ FastRetry = "não completar."
+ RestartRequired = "completa. Uma reinicialização é necessária."
+ Start = "começou a."
+ }
+ BlockExecution = @{
+ Message = "Lançar este aplicativo está temporariamente bloqueado para que possa concluir uma operação de instalação."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Fechar Programas"
+ ButtonContinue = "Continuar"
+ ButtonContinueTooltip = "Selecione `"Continuar`" somente após fechar a(s) aplicação(ões) listada(s) abaixo."
+ ButtonDefer = "Adiar"
+ CountdownMessage = "NOTA: O programa será fechado automaticamente em:"
+ Message = "Programas de seguir devem ser fechados antes que a instalação possa prosseguir.`n`nPor favor, guarde o seu trabalho, feche os programas e em seguida continuar. Como alternativa, salve seu trabalho e clique em `"Fechar Programas`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Prazo:"
+ ExpiryMessage = "Você pode optar por adiar a instalação até que expire o diferimento:"
+ RemainingDeferrals = "Restantes diferimentos:"
+ WarningMessage = "Uma vez que o diferimento expirou, você já não terá a opção de adiar a."
+ WelcomeMessage = "O seguinte aplicativo está prestes a ser instalado:"
+ }
+ DeploymentType = @{
+ Install = "Instalação"
+ Repair = "Reparação"
+ Uninstall = "Desinstalação"
+ }
+ DiskSpace = @{
+ Message = "Você não tem espaço em disco suficiente para concluir a instalação de:`n{0}`n`nEspaço necessário: {1}MB`nEspaço disponível: {2}MB`n`nPor favor, espaço livre em disco suficiente, a fim de prosseguir com a instalação."
+ }
+ Progress = @{
+ MessageInstall = "Instalação em andamento. Por favor aguarde..."
+ MessageInstallDetail = "Esta janela fechar-se-á automaticamente quando a instalação estiver concluída."
+ MessageRepair = "Reparação em andamento. Por favor aguarde..."
+ MessageRepairDetail = "Esta janela fechar-se-á automaticamente quando a reparação estiver concluída."
+ MessageUninstall = "Desinstalação em andamento. Por favor aguarde..."
+ MessageUninstallDetail = "Esta janela fechar-se-á automaticamente quando a desinstalação estiver concluída."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimizar"
+ ButtonRestartNow = "Reinicie Agora"
+ Message = "Em ordem para completar a instalação, você deve reiniciar seu computador."
+ MessageRestart = "Seu computador será reiniciado automaticamente no final da contagem regressiva."
+ MessageTime = "Por favor, salve o trabalho e reiniciar no tempo alocado."
+ TimeRemaining = "Tempo restante:"
+ Title = "Reinicialização Necessária"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "O {0} continuará automaticamente em:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Aplicação {0}'
+ DialogMessage = 'Guarde o seu trabalho antes de continuar, pois as aplicações seguintes serão encerradas automaticamente.'
+ DialogMessageNoProcesses = 'Selecione Instalar para continuar com a instalação. Se ainda tiver algum adiamento, também pode optar por adiar a instalação.'
+ ButtonDeferRemaining = 'permanecer'
+ ButtonLeftText = 'Adiar'
+ ButtonRightText = 'Fechar aplicações e instalar'
+ ButtonRightTextNoProcesses = 'Instalar'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBx4CFi6bOVl6IU
+# ySQPwipZDfg5qg2V9TSLlsl0Snd2I6CCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQg8sNoNJldLm+KnF1FWKujGkFzbdYBSdVicN1ZSXRDXg0w
+# DQYJKoZIhvcNAQEBBQAEggGAkBA1c8Ooej8XYrVq7jatDEDzZOGMiI6l/asZGhFm
+# JmzNWtPKuC59f1YLho21bnBsfBsUlZuANxKCzDN+J4Ok01r5GzZqBoENIryOL0ov
+# zv8SGt9DO+Qz7AJ+6fAaNlSuXZkAV9mRFuas/opJglvmSlQnPrKjOD/lc6TMQTS3
+# JpJeDpkh1jynY5ZI4HfhG7i0NWeg/Pl/vPsh1FzscyUpkS2AwtlLvrtckXM89BWl
+# RXrK362mZ984yrp2wjVBa/QN8PB+CL3WuaYaR2vB+Agq3vPxJKoCuy+E4Ox/exII
+# QBI2WRAS5IWRDlGu4DBULxyOgP3Vfe3fPDlp+/jbP20XcEjsaWPdGbPUEGP6WwlF
+# sjrsbubC1s3fVCxC7694ltkEcZLNpt6nEU4W6QeFSJ2cBLp2tJk+IHfTAsoUf/94
+# jhHJct8jw836XYxHwEUX+4hjy7zvf9gon/AlBw3GrS33fer8pJHSpHH53JjE5szt
+# 3mGHmdVB7vNXDqManxQLdfj4oYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEILF0HQoH
+# xd+7Zmwk7V+rGV+hAAR7LkWkf8M675RwvraFAhBJFzsm0nhd4RSIK5IQlEo4GA8y
+# MDI1MDIyMzIzNDgxMFqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgxMFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIBILwHTAOUDXa8jwsmntAMT7nS+nzfRPBylqvUij
+# XZaOMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAEjRlvyhFOH6unZMuoyXlA+D
+# 1M2ifdDAHo/q+pjTa2Q/nIt//ggvqxY17n2PUUkp0iFVqiV9aQhaOdAiiG3nFFZc
+# FF9/6gxDeSC3ROpVXv7RhAoomo8gn6mxO0+S0njOEA50klhIIfikeeTS1HaPbIJ6
+# K5fx7HNHU0Bw7RHnGNw2oJa1gtigwjAOQqaATo7QWtGiLwlDhI30CLKONXN+QcCg
+# fe90hj3pVLuV78twig9+6dwp26UD82Iy1nRdioRNSj/2Adc1C65kG8NzLoG8DRuQ
+# GoUIvjPTqngKQU56dRFAAOg3GQC8CAtIakIa/sDGSCh6P41YYaJkjUEqJfEL3RCc
+# Vf4lmM7PJhew6vLo0jyj8vZ75nr5k9y1Zy7pr/W8HjqCdRShIUqsrukdWpBZ7xz8
+# Px6KMSJgY2iTrHo3rjVxX8dfdazaNKffVriqzW+6M2CSt2p4EnkrGknNXu4i+f2G
+# j9h+jl/dW+GS3XiE+MAmJwncIu3lhZRwQ3M9B1xN9STMH7IUEJjO09ltFmU/lVX/
+# 5CN/Ppya7XhtX+U6V+WBqKFAqd3B9NmcEtjSoU/olr8IKZ5/L7OkTgVTHI2/mHXL
+# QK5XwJSR/KdbZZ+B1Wv9t+svAGowZmljaZXrHBg3EvqrhgrjsC5/USH+qklDqUtE
+# uF/SSkDnQon+YN9vUNfh
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/ru/strings.psd1 b/PSAppDeplyToolkit/Strings/ru/strings.psd1
new file mode 100644
index 0000000..1d997a1
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/ru/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "выполнена(о)."
+ Error = "не выполнена(о)."
+ FastRetry = "не завершена(о)."
+ RestartRequired = "выполнена(о). Требуется перезагрузка."
+ Start = "начата(о)."
+ }
+ BlockExecution = @{
+ Message = "Запуск этого приложения временно заблокирован для завершения процесса установки."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Закрыть программы"
+ ButtonContinue = "Продолжить"
+ ButtonContinueTooltip = "Выберите `"Продолжить`" только после закрытия вышеперечисленных приложений."
+ ButtonDefer = "Отложить"
+ CountdownMessage = "ПРИМЕЧАНИЕ: Эти программы будут автоматически закрыты через:"
+ Message = "Перед продолжением установки необходимо закрыть следующие программы.`nПожалуйста, сохраните вашу работу и закройте программы, а затем продолжите установку. Также вы можете сохранить вашу работу и нажать `"Закрыть программы`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Дата истечения:"
+ ExpiryMessage = "Вы можете отложить установку приложения до тех пор, пока не истечет срок действия этой отсрочки:"
+ RemainingDeferrals = "Оставшиеся отсрочки:"
+ WarningMessage = "После истечения срока действия отсрочки вы больше не сможете отложить установку."
+ WelcomeMessage = "Планируется установка следующего приложения:"
+ }
+ DeploymentType = @{
+ Install = "Установка"
+ Repair = "Исправление"
+ Uninstall = "Удаление"
+ }
+ DiskSpace = @{
+ Message = "У вас недостаточно пространства на диске для выполнения установки:`n{0}`n`nНеобходимое пространство на диске: {1}МБ`nДоступное пространство на диске: {2}МБ`n`nДля продолжения установки, пожалуйста, освободите достаточно пространства на диске."
+ }
+ Progress = @{
+ MessageInstall = "Идет установка. Пожалуйста, подождите..."
+ MessageInstallDetail = "Это окно закроется автоматически после завершения установки."
+ MessageRepair = "Идет исправление. Пожалуйста, подождите..."
+ MessageRepairDetail = "Это окно автоматически закроется по завершении ремонта."
+ MessageUninstall = "Идет удаление. Пожалуйста, подождите..."
+ MessageUninstallDetail = "Это окно закроется автоматически после завершения удаления."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Минимизировать"
+ ButtonRestartNow = "Перезагрузить сейчас"
+ Message = "Для завершения установки необходимо перезагрузить ваш компьютер."
+ MessageRestart = "Ваш компьютер будет автоматически перезагружен по завершению обратного отсчета."
+ MessageTime = "Пожалуйста, сохраните вашу работу и выполните перезагрузку в отведенное время."
+ TimeRemaining = "Оставшееся время:"
+ Title = "Требуется перезагрузка"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} автоматически продолжится через:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - приложение {0}'
+ DialogMessage = 'Пожалуйста, сохраните свою работу, прежде чем продолжить, так как следующие приложения будут закрыты автоматически.'
+ DialogMessageNoProcesses = 'Чтобы продолжить установку, выберите Install (Установить). Если у вас остались отсрочки, вы также можете отложить установку.'
+ ButtonDeferRemaining = 'оставаться'
+ ButtonLeftText = 'Отложить'
+ ButtonRightText = 'Закрыть приложения и установить'
+ ButtonRightTextNoProcesses = 'Установите'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCi2dJD/IrA1DNV
+# 83ltMdcAgYVFji2FSt7FFjKfwcRGpKCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgzhY+gOco6jEukG5ISuvAt1PScVahkQg0yrdNm3y5Apow
+# DQYJKoZIhvcNAQEBBQAEggGAk5Ra9ccDFS2UR0k2FFRy3t6fy+4YpyTH6+yZDIyg
+# YDVFb9fUL9eTgs16T2ubgURldZNqcoX2AS7jVGH97L+su2lpRvH6O8viVfE/pbIF
+# aUrvpPoBnlNvqpweDnM8gZUz8V7KMHdguDpxzX/mCCrCZ2zHtcpiOuVtoqgKg8bs
+# TmmptMJv9d9QO7hiSfhpVRTaI/zLfnQGyppIPJaoCFFUO3XR3PfoGpt8gHpJwwFS
+# IpK3u5XDNDC7TctsCyIyx2BcqAoNuZwWVTnPwTL4qXm0/GJ+DvoDsQjoyhhGxBiI
+# JqD/FZxwAHVDGLsTcwP5AKLdtCF8MXkYOmfyCDYGrveg+Id3g026QyWnWObxrEP1
+# CWCeVm1ZAVtnSepce0FS1DAM3B06hvloHMI0EZM0tk06j4ujKTewoQHI0h32M1q8
+# uZCFwOQnEzwoG+QgMOYZ87EM2XJvQxmzhVGs2poMQV0ZB0bGFnDt8Cy0FtnxHVnH
+# USlFJExnBvPzHay8H98IA8IVoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIEuIjwqw
+# 08Z6xLtaWh1Rj4gxzvBwUQ2crlG5QqO0ssuvAhBLBtXp7hjTBval3mHrxeA/GA8y
+# MDI1MDIyMzIzNDgxNlqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgxNlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIEYBWJnZBrHRQjxjynz5efyGTObbyrNsop3tOIdK
+# Yiy9MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICACVaquVy1vL94QuwJf38F3OM
+# 50EA7TZMyoSMJN1rGyH/KETb6ALW0AJS7c6C4+WHwdF64jLV5ru9iuH/4WMZ5ppC
+# PM2I2VnTY8da7p1HF+pKuMHifqCdOgIqJUh1b8qst3PWAdkBhm6CjLxELvddUYl+
+# 4tBF6uRo9ZEmP2xM6TfUxZCGV+im6HgDVQZRWe3ZZKoid2ihBiOHpxo7m8TlHJkf
+# meRvvsk00cyweOwRGw0DHA4U2cZ40s6DkgLrHDr4tOJoo6DdGU0b5kSd2UDYpspB
+# gqqwNxzG1pQ2c+9Q/tyJ3l0x+EQqvhTHpAJfTnpwS0rQ1CMfjUCwJvCz7982pB0O
+# kkXZzO+Wg5GMOyaojXqCEg6xy2tty1cfMQ1qj+QNvP+uQnWVV5KpVhajGnT+GGUI
+# uDWtqRcxpgePkkos2rHoA1F4ZuwoBcU10RvcH5ipF9tIbvaN4pugjsS+c1MDeyNA
+# zKnRG93IH/sj4wXeOKh6Pm0/xx5wxUbsJoCFkUl8mRH9sfHUN9WI3f+oAm3qhmtC
+# upKbd7g21R6OAFKasSF4dy8vVpYuEs//o2EHHUN5iqwPtkM/rVbJTSiIqBB/rYXa
+# MHpyqcUR4HFeSzjJ3VHEQePX5P6NGp4MYdiswyItqQByIOCaH0QKIo/F6MYcbdEL
+# 4gEEm1Mg3GknnVRRa/v1
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/sk/strings.psd1 b/PSAppDeplyToolkit/Strings/sk/strings.psd1
new file mode 100644
index 0000000..0260e89
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/sk/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "ukončená."
+ Error = "sa nepodarila."
+ FastRetry = "nedokončená."
+ RestartRequired = "ukončená. Je nutný reštart."
+ Start = "spustená."
+ }
+ BlockExecution = @{
+ Message = "Spustenie tejto aplikácie bolo dočasne zablokované, aby mohla byť inštalácia dokončená úspešne."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Ukončiť programy"
+ ButtonContinue = "Pokračovať"
+ ButtonContinueTooltip = "Kliknite na `"Pokračovať`", keď zavriete vyššie uvedené aplikácie."
+ ButtonDefer = "Oddialiť"
+ CountdownMessage = "Poznámka: Programy budú automaticky ukončené za:"
+ Message = "Nasledujúce programy musia byť zatvorené, než bude inštalácia pokračovať.`n`nProsím, uložte svoju prácu, zatvorte dané programy a potom kliknite na pokračovať. Prípadne môžete uložiť svoju prácu a potom kliknite na tlačidlo `"Ukončiť programy`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Termín:"
+ ExpiryMessage = "Inštaláciu môžete niekoľkokrát odložiť:"
+ RemainingDeferrals = "Zostávajúce odklady:"
+ WarningMessage = "Akonáhle odklady uplynú, už nebudete mať možnosť odložiť inštaláciu."
+ WelcomeMessage = "Nasledujúca aplikácia bude nainštalovaná:"
+ }
+ DeploymentType = @{
+ Install = "Inštalácia"
+ Repair = "Oprava"
+ Uninstall = "Odinštalácia"
+ }
+ DiskSpace = @{
+ Message = "Nemáte dostatok voľného miesta na dokončenie inštalácie:`n{0}`n`nPotrebné miesto: {1}MB`nVoľné miesto: {2}MB`n`nProsím, uvoľnite dostatok miesta pre pokračovanie inštalácie."
+ }
+ Progress = @{
+ MessageInstall = "Inštalácia sa vykonáva. Prosím čakajte..."
+ MessageInstallDetail = "Toto okno sa po dokončení inštalácie automaticky zatvorí."
+ MessageRepair = "Vykonáva sa oprava. Prosím čakajte..."
+ MessageRepairDetail = "Toto okno sa po dokončení opravy automaticky zatvorí."
+ MessageUninstall = "Prebieha odinštalácia. Prosím čakajte..."
+ MessageUninstallDetail = "Toto okno sa po dokončení odinštalovania automaticky zatvorí."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimalizovať"
+ ButtonRestartNow = "Reštartovať Teraz"
+ Message = "Na dokončenie inštalácie musíte váš počítač reštartovať."
+ MessageRestart = "Na konci odpočítavania, bude váš počítač automaticky reštartovaný."
+ MessageTime = "Prosím, uložte si prácu a reštartujte počítač v stanovenej lehote."
+ TimeRemaining = "Zostávajúci čas:"
+ Title = "Je nutný reštart."
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} bude automaticky pokračovať za:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Aplikácia {0}'
+ DialogMessage = 'Pred pokračovaním uložte svoju prácu, pretože nasledujúce aplikácie sa automaticky zatvoria.'
+ DialogMessageNoProcesses = 'Ak chcete pokračovať v inštalácii, vyberte možnosť Inštalovať. Ak máte ešte nejaké odklady, môžete tiež zvoliť odloženie inštalácie.'
+ ButtonDeferRemaining = 'zostať'
+ ButtonLeftText = 'Odloženie'
+ ButtonRightText = 'Zatvoriť aplikácie a nainštalovať'
+ ButtonRightTextNoProcesses = 'Inštalácia stránky'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDjnuJh/xynnhZG
+# ZM1jcpAOXvICv+Rnbj3fJZGanmQq/qCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgyTaBUqm2vtcPnr6Oab/l4NqdM/xCHOi2+lxhYQyPWcow
+# DQYJKoZIhvcNAQEBBQAEggGAkrh32aixxU9t+mwle48i94zDw4DrBI+ovZ/upn4K
+# 1SH8cyqHZsVV4DB3yLqcaSxO3LOA+yA2E/t6EApiM1aY7cXGVccTuupNA5udh3pQ
+# RDr+AqN2kfdN7rgWn1Mv8et0GahwsO0NqFyNp7fbd42sF1bZW4QFaZ+wlC5bgvtA
+# M26C1f79YkHk+wK2kf2iDGpqRaA1WqG3X42wmNK/8AXb0tHaTLowhtGgQmR/4hOw
+# F+sTpYPsw4DUzVy6zJE1vL9ONHkjEfscHACYA4H+1HQ6oVWilGJNn9I7eGD2egNg
+# x/fmA/RLI2O/hi8mam3XDJhCMmC73Ht9AjMIZPI43qjXUYcfEyo3MWlbKknishAm
+# aa5rqHd6rcTgQIat8z1RKp3U7gjOzp/eTONh3klr9m8zijF32JO0ysxDmy2D8ink
+# BztYTMZuMnwuVE6ciMFMDzh5SLAOf5fUZGAUu/Z5yb0OHwIN40VeRgw1S0otoL6u
+# afE95sMbBiXwwL5RqXe24jDuoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIHai5uS5
+# xUm7ZA/vugMTU77JP8B6wtZr16jXqu64Y5VeAhANWFxmL68D69X1/f2VFQlUGA8y
+# MDI1MDIyMzIzNDgxOVqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgxOVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIM4qpBAfpf6IQRwWSlvrNljK2rN+YT99WeYxlBds
+# xxKbMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICACmgRNztFdiP8tOw4de9AJuF
+# XRYI2acKNFkCioOAqWx/+jwIZFE/Fe7xwU2hrtTm4XHn86FxM5ec2mrVU2CKlDhN
+# 3bgojPtrV/35o9uHrJ9lOeLbmpxZrooskvLkK5QDWhLfXiP37FsU8RbIUFNzQJ8U
+# yUQfWO/EjBfIE/xlJvCze7ISscny8gLEEGC/6KC7HBbyaeTNXmowe/v2EW0wShIz
+# S5oMIrQndsYAajn9Gc9kSpp/S77Ae8FhqrVMV6ejRw/WnMJ11ro2U50xYkF+y7NY
+# iY4Bn9R5fH4CnkelPpNX3NyI+N5XONVb1U2onRV1IVQldGbnaT9RQdi1m3zoD1fo
+# K2a5PcmlH9eHv7/Lr6sHQK7jJ0jFthXUE9AYu+GDaoMNw+MiYWfu8WE6g9WH92b/
+# iIEvcDc+WD2BAa8+2siYgXoPl4BI3mmsfSr2KBug1uTtFoF0eALAxvmlWZmzqQDY
+# kDLs2H0ZrIuUDwMR9lDyY1iSuYw5LVKj5Vqcmt6FvtzbbzJnbPYo8SdqLuFAfb80
+# Ee0MNhCa3FULmsGITslLap946PYDoORboXLsSoIWxnXMUJpULQtLItf6+BEUIn8S
+# RMq2pD7aLh8srGOWmFv29FLERlD315vBJrH1OpifHdT12Dlpbk0yyv1lb+1+1Bly
+# kRB+oH3p+CW4rpDF6ejv
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/strings.psd1 b/PSAppDeplyToolkit/Strings/strings.psd1
new file mode 100644
index 0000000..d176d0b
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/strings.psd1
@@ -0,0 +1,401 @@
+@{
+ BalloonText = @{
+ # Text displayed in the balloon tip for successful completion of a deployment type.
+ Complete = 'complete.'
+
+ # Text displayed in the balloon tip for a failed deployment type.
+ Error = 'failed.'
+
+ # Text displayed in the balloon tip for fast retry of a deployment.
+ FastRetry = 'not complete.'
+
+ # Text displayed in the balloon tip for successful completion of a deployment type.
+ RestartRequired = 'complete. A reboot is required.'
+
+ # Text displayed in the balloon tip for the start of a deployment type.
+ Start = 'started.'
+ }
+
+ BlockExecution = @{
+ # Text displayed when prompting user that an application has been blocked.
+ Message = 'Launching this application has been temporarily blocked so that an installation operation can complete.'
+ }
+
+ ClosePrompt = @{
+ # Text displayed on the close button when prompting to close running programs.
+ ButtonClose = 'Close &Programs'
+
+ # Text displayed on the continue button when prompting to close running programs.
+ ButtonContinue = '&Continue'
+
+ # Tooltip text displayed on the continue button when prompting to close running programs.
+ ButtonContinueTooltip = "Only select `"Continue`" after closing the above listed application(s)."
+
+ # Text displayed on the defer button when prompting to close running programs.
+ ButtonDefer = '&Defer'
+
+ # Text displayed when counting down to automatically closing applications.
+ CountdownMessage = 'NOTE: The program(s) will be automatically closed in:'
+
+ # Text displayed when prompting to close running programs.
+ Message = "The following programs must be closed before the installation can proceed.`n`nPlease save your work, close the programs, and then continue. Alternatively, save your work and click `"Close Programs`"."
+ }
+
+ DeferPrompt = @{
+ # Text displayed when there is a specific deferral deadline.
+ Deadline = 'Deadline:'
+
+ # Text displayed when a deferral option is available.
+ ExpiryMessage = 'You can choose to defer the installation until the deferral expires:'
+
+ # Text displayed when there are a specific number of deferrals remaining.
+ RemainingDeferrals = 'Remaining Deferrals:'
+
+ # Text displayed after the deferral options.
+ WarningMessage = 'Once the deferral has expired, you will no longer have the option to defer.'
+
+ # Text displayed when only the deferral dialog is to be displayed and there are no applications to close.
+ WelcomeMessage = 'The following application is about to be installed:'
+ }
+
+ DeploymentType = @{
+ # Name displayed in UI for installation deployment type.
+ Install = 'Installation'
+
+ # Name displayed in UI for repair deployment type.
+ Repair = 'Repairing'
+
+ # Name displayed in UI for Uninstallation deployment type.
+ Uninstall = 'Uninstallation'
+ }
+
+ DiskSpace = @{
+ # Text displayed when the system does not have sufficient free disk space available to complete the installation.
+ Message = "You do not have enough disk space to complete the installation of:`n{0}`n`nSpace required: {1}MB`nSpace available: {2}MB`n`nPlease free up enough disk space in order to proceed with the installation."
+ }
+
+ Progress = @{
+ # Default text displayed in the progress bar for installations.
+ MessageInstall = 'Installation in progress. Please wait...'
+
+ # Default text displayed in the progress bar for installations.
+ MessageInstallDetail = 'This window will close automatically when the installation is complete.'
+
+ # Default text displayed in the progress bar for repairs.
+ MessageRepair = 'Repair in progress. Please wait...'
+
+ # Default text displayed in the progress bar for repairs.
+ MessageRepairDetail = 'This window will close automatically when the repair is complete.'
+
+ # Default text displayed in the progress bar for Uninstallations.
+ MessageUninstall = 'Uninstallation in progress. Please wait...'
+
+ # Default text displayed in the progress bar for Uninstallations.
+ MessageUninstallDetail = 'This window will close automatically when the uninstallation is complete.'
+ }
+
+ RestartPrompt = @{
+ # Button text for allowing the user to restart later.
+ ButtonRestartLater = 'Minimize'
+
+ # Button text for when wanting to restart the device now.
+ ButtonRestartNow = 'Restart Now'
+
+ # Text displayed when the device requires a restart.
+ Message = 'In order for the installation to complete, you must restart your computer.'
+
+ # Text displayed when indicating when the device will be restarted.
+ MessageRestart = 'Your computer will be automatically restarted at the end of the countdown.'
+
+ # Text displayed as a prefix to the time remaining, indicating that users should save their work, etc.
+ MessageTime = 'Please save your work and restart within the allotted time.'
+
+ # Text displayed to indicate the amount of time remaining until a restart will occur.
+ TimeRemaining = 'Time remaining:'
+
+ # Text displayed in the title of the restart prompt which helps the script identify whether there is already a restart prompt being displayed and not to duplicate it.
+ Title = 'Restart Required'
+ }
+
+ WelcomePrompt = @{
+ Classic = @{
+ # The countdown message displayed at the Welcome Screen to indicate when the install will continue if no response from user.
+ CountdownMessage = 'The {0} will automatically continue in:'
+
+ # This is a custom message to display at the Welcome Screen window.
+ CustomMessage = ''
+ }
+ Fluent = @{
+ # The subtitle underneath the Application Title, e.g. Company Name. Using {0} will insert the Application Type, e.g. App "Install"
+ Subtitle = 'PSAppDeployToolkit - App {0}'
+
+ # This is a message to prompt users to save their work.
+ DialogMessage = 'Please save your work before continuing as the following applications will be closed automatically.'
+
+ # This is a message to when there are no running processes available.
+ DialogMessageNoProcesses = 'Please select Install to continue with the installation. If you have any deferrals remaining, you may also choose to delay the installation.'
+
+ # This is a word used to describe the number of deferrals left. custom message to display at the Welcome Screen window.
+ ButtonDeferRemaining = 'remain'
+
+ # This is a phrase used to describe the process of deferring an application installation.
+ ButtonLeftText = 'Defer'
+
+ # This is a phrase used to describe the process of closing applications and installing the application.
+ ButtonRightText = 'Close Apps & Install'
+
+ # This is a phrase used to describe the process of installing the application.
+ ButtonRightTextNoProcesses = 'Install'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDceA32Kn94Ko4r
+# JPkYaKYPFcqZE31XFSHfazKWkIpQaqCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgFEg4Jh0XisAdDJYdBIJXgg7djD7m96fqxYC3yH8TfVww
+# DQYJKoZIhvcNAQEBBQAEggGAmbsFqaErBEWR5X5aiqUksCAxY+3TicS5uGMLNIwg
+# bj8huksFVI3DvpPltFKqtKed39PZc9LWOiaPMgx7dE/9EEnxEh7fqtVrGm76DaJJ
+# hYodnjxfuxsS575eLYhVygqK7C6OCD9z10mBzSKVCDR5Rsu5ANLGAxeDpmUIfQns
+# ZhyZjFCgp9T+tswvgWAT/1X0axN1jOtzsrrcK/r/ftBY7kLseogPdvew41K2Uhfv
+# 9o+SVmyqv2GKPVwm3vlY4szgT9/SbCxmyg7/rECrQth/hMFE6H1FKrYw1AuIhVUF
+# yZFmtWajtlH15nBWOEOGcvwqafzodRH7WQoALWaAU5bRu1dF64UD1ftBZfy25855
+# 53zhQHLK8kk5VYIkgIcXV6/9MXUAFe7iNotRLZ5mO6D3Pg55cH4mysMD+pN1NvXY
+# 80hgoyXNPPn9FqhKwmg3YXsqtxhCGpsZJZFiGsFtD9XZXbV16/hpK8drqI1KVEM3
+# bYwqsmKbGCQbScFNGwUqUyZWoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIBcD7mhl
+# aUZS7xadVuEb+bqSu+AluIOz7UvoDYrqu/s/AhBBGwVpLbE8WrsnS+preM3+GA8y
+# MDI1MDIyMzIzNDgzMlqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgzMlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIFn2MBhbwmMQptXZtpkLrdyFeyVmHn4P1ehPcFtU
+# proaMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICAKbZLYdtQ+pYtf3Aj84UN/I/
+# 8KbLy9o+Qbsp2jsvRTguvUqjzE6F9QIoi8ijkVEFDYkOzE5ywNM+xoIu1R6c5wVB
+# sojyltcv9fqwZ0luHgke3IBOKepveCc7DTpkWoQrmmrWIWYgW5lB/4Rc6Auy7pUb
+# KfremDALeU8fOttB0z5SqPX8aTNF9C1XXcvZDKNyJs793Kdb4MHX3zaRJPZuaznh
+# +AYbVKKLEfBPJoIXYpNha56Jo5DlAdDXBVIVuDLdeFCTZMD3heWyQ9Dj9iD/+98m
+# 1YATtcOxO5u5fgSQDoollMvYCd1/x4kGd2ZNNjzEE6T+yKhLxMVNDL1xGqleCr2a
+# yrWcdPlm7iYa5r4gnLknm/3XhYnTMSCAlCaH6nWlclgWYdCpq/UX51NF+OIpWooG
+# JRt1v5Ey8yVcuiHIUNRoo1y1nXeqHTXab/r9sbz4WOGdVdYfo8UMQ1VPT8KgKleU
+# vIimTmNE7zeORYfdcNfJGkVcIINqgovDLZ34qcrsSEjkChSg/O/uMCO6iOYVaPSH
+# 9SR8tN71sAV16k54jEbqcOXnxvKUzRMlUZR4euGF6LyBMPIcjAE5CuSR15GKGpcB
+# ARqZLBGxSsoloOPW2unt4DYeOPCHDsKljzHXJIF7huVYFHzFAQUauDKkvwxP/4cf
+# FMrK8MmY7WHbRehRbhtM
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/sv/strings.psd1 b/PSAppDeplyToolkit/Strings/sv/strings.psd1
new file mode 100644
index 0000000..da221ee
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/sv/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "slutförd."
+ Error = "misslyckades."
+ FastRetry = "ej slutförd."
+ RestartRequired = "slutförd. En omstart av datorn är nödvändig."
+ Start = "startad."
+ }
+ BlockExecution = @{
+ Message = "Den här applikationen har temporärt blockerats så att installationen kan slutföras."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Stäng Program"
+ ButtonContinue = "Fortsätt"
+ ButtonContinueTooltip = "Välj `"Fortsätt`" endast efter att ha stängt applikation(er) i ovanstående lista."
+ ButtonDefer = "Skjut upp"
+ CountdownMessage = "OBS: Programmen kommer automatiskt att avslutas om:"
+ Message = "Följande program måste stängas innan installationen kan fortsätta.`n`nSe till att spara ditt arbete, stäng de öppna programmen och klicka sen på `"Fortsätt`".`nAlternativt, spara ditt arbete och klicka på `"Stäng Program`"."
+ }
+ DeferPrompt = @{
+ Deadline = "Deadline:"
+ ExpiryMessage = "Du kan välja att fördröja installationen ett begränsat antal gånger under en begränsad tid:"
+ RemainingDeferrals = "Antal återstående fördröjningar:"
+ WarningMessage = "När antalet fördröjningar är slut eller deadlinen inträffar är detta alternativ inte längre tillgängligt."
+ WelcomeMessage = "Följande applikation kommer att installeras:"
+ }
+ DeploymentType = @{
+ Install = "Installation"
+ Repair = "Reparation"
+ Uninstall = "Avinstallation"
+ }
+ DiskSpace = @{
+ Message = "Du har inte tillräckligt med ledigt diskutrymme för att kunna installera:`n{0}`n`nDiskutrymme som krävs: {1}MB`nLedigt diskutrymme: {2}MB`n`nFrigör utrymme på hårddisken och försök igen."
+ }
+ Progress = @{
+ MessageInstall = "Installation pågår. Var god vänta..."
+ MessageInstallDetail = "Detta fönster stängs automatiskt när installationen är klar."
+ MessageRepair = "Reparation pågår. Var god vänta..."
+ MessageRepairDetail = "Detta fönster stängs automatiskt när reparationen är klar."
+ MessageUninstall = "Avinstallation pågår. Var god vänta..."
+ MessageUninstallDetail = "Detta fönster stängs automatiskt när avinstallationen är klar."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Minimera"
+ ButtonRestartNow = "Starta Om Nu"
+ Message = "För att installationen ska kunna slutföras måste din dator startas om."
+ MessageRestart = "Din dator kommer automatiskt att starta om när nedräkningen är slut."
+ MessageTime = "Se till att spara ditt arbete innan tiden går ut och en automatisk omstart sker."
+ TimeRemaining = "Återstående tid:"
+ Title = "Omstart Krävs"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} kommer att fortsätta automatiskt i:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - App {0}'
+ DialogMessage = 'Spara ditt arbete innan du fortsätter eftersom följande applikationer kommer att stängas automatiskt.'
+ DialogMessageNoProcesses = "Välj Installera för att fortsätta med installationen eller välj `"Skjut upp`" för att installationen skall utföras vid ett senare tillfälle."
+ ButtonDeferRemaining = 'kvarstår'
+ ButtonLeftText = 'Skjut upp'
+ ButtonRightText = 'Stäng appar och installera'
+ ButtonRightTextNoProcesses = 'Installera'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBvWZaXMxbvaLKb
+# ygNul3R4rdRrZTHM8p1s2wdqKgYG2aCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgIE23yTxbVxcG+CTEUYujA0jkP/OkBt6BOKsPPayHu3Aw
+# DQYJKoZIhvcNAQEBBQAEggGAFH9Lg2bh116YjUx21qRXfQ1Yt/t69Rfn7mwq6M+k
+# fgm4v5gHMGMCuKJTs710vlhvmKxLgNVqTPcF3fQ+gdaC3u6Fiow6Hh5lDa1vqR2O
+# gd93VIYJ0SnwEJob1E3ORpluDmitGiD1zlZwxkEtbxoApaYJMw0X3Ri58iqvbo+u
+# VcaPQAw54WUX8PdUS/ohR534BVW8NKKq0kUelwsKO8DVJX2KZ5sKC0ZKJbPzgBZ0
+# amops7sbsXwOw4nSrgaTByChyQVFvt1BLd3Wv5CjwbXJXN8Gh+Aei/3wuGTATnJr
+# TeqYePcKMjQjNX4n9FTg8l84QZaCq1BtLqOQbdjBM6A8Tw4TYNd9F4iQc1F5iScT
+# Oj/fYu0MAOyZBROD9nt8nmqCdFvoNcTlOYTBLY4i3Mc0wF1BC+6ao+6HvhJ3Y2lm
+# uaUd5C//lZ2oy76i9CwxErWSdah6T5ZEkTCDQ2f1u/fny/tSyn0gJUslpvlcrjZm
+# /v6ZWNsVn/vnvqm87aBD0YkuoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIISjN2qQ
+# VwEE3W8soDM4W2MGSR7kkoz0yIHnfavs0AfhAhBBeKn4w70/HZYbvw/uAXmRGA8y
+# MDI1MDIyMzIzNDgyMVqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgyMVowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIJX6N2tV42XKJaWpEzYf2gXf1xKHsFvu/H6lbeoL
+# YNA4MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICALUCWgRR0vYeCZ/SZFENtB3p
+# UGCpG3lwMziRTyjmRU1XyP37Im4oAtQ6x7hM7tnPk7qf4+hiNGWsIU6xse3eVNS6
+# qrdxvn3qmsDpWM/iuh/AEMHXXWZNmb/K+yBawFgP6PDy3wVpRzY+Z8hLt/N7SZNO
+# DX3+CaSDZgAPAFTJojxhY29tbX2h2RJ6fn7Pf6RXCRMWvyVhbwSIZuyL1RUliTm1
+# txDqLCbjSNLrYhrsD5Y2MvsFxL14hb+2jp72+xCuLXtCCNatQ6rqsCeEe7kzDAmJ
+# kymHJZM8q2zY31IrdKiqEwv5EEVWTh5OleNsoADcovooA9wdWdVyNq9aSxUWWABQ
+# w3TA5dPweR+RDBLYvNiXLV1MqdEGtnvrvr/inGb1f7r98vNGk1Rt+dvVr/RCmw2u
+# 3qkbfjHGL7HHbM8Bg2D4SGN4Q7uA6oVNVonq8tuODzwP+DtyNo3y6KfBfFmdWsBq
+# KkUPEJ9oN1GKcpsn80ZAEYUjy3nr3McT07gbUP2hpmFu50MODDY55SFenmGz93XT
+# edKnfpuxv+irNtEw+ruqPnHTtHERdcdvxel0puGOeIxfl+wzXWP4DkvechNL5QFL
+# s2ufFLz1tW2oMgF0A8FRgSSgMEexOE1C6P9/+6DmSl07dV4AZzNUxCn8m9830HDh
+# JKxy6sqkWHhy5GkvnTn7
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/tr/strings.psd1 b/PSAppDeplyToolkit/Strings/tr/strings.psd1
new file mode 100644
index 0000000..16d638c
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/tr/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "tamamlandı."
+ Error = "hata oluştu."
+ FastRetry = "tamamlanamadı."
+ RestartRequired = "tamamlandı. Yeniden başlatma gereklidir."
+ Start = "başladı."
+ }
+ BlockExecution = @{
+ Message = "Yükleme işleminin tamamlanabilmesi için bu uygulamanın başlatılması geçici olarak engellenmiştir."
+ }
+ ClosePrompt = @{
+ ButtonClose = "Uygulamaları kapat"
+ ButtonContinue = "Devam et"
+ ButtonContinueTooltip = "Aşağıdaki listedeki uygulamaları kapatıp `"Devam et`"i seçiniz."
+ ButtonDefer = "Ertele"
+ CountdownMessage = "NOT: Program(lar) otomatik olarak kapanacaktır:"
+ Message = "Kurulumun devam edebilmesi için aşağıdaki programlar kapatılmalıdır.`n`nLütfen çalışmanızı kaydedin, programları kapatın ve ardından devam edin.`nAlternatif olarak, çalışmanızı kaydedin ve `"Programları Kapat `"a tıklayın."
+ }
+ DeferPrompt = @{
+ Deadline = "Son tarih:"
+ ExpiryMessage = "Erteleme süresi dolana kadar kurulumu ertelemeyi seçebilirsiniz:"
+ RemainingDeferrals = "Kalan Ertelemeler:"
+ WarningMessage = "Erteleme süresi sona erdiğinde, artık erteleme seçeneğiniz olmayacaktır."
+ WelcomeMessage = "Aşağıdaki uygulama yüklenmek üzeredir:"
+ }
+ DeploymentType = @{
+ Install = "Kurulum işlemi"
+ Repair = "Onarım"
+ Uninstall = "Kaldırma işlemi"
+ }
+ DiskSpace = @{
+ Message = "Kurulumu tamamlamak için yeterli disk alanınız yok:`n{0}`n`nGerekli alan: {1}MB`nMevcut alan: {2}MB`n`nKuruluma devam etmek için lütfen yeterli disk alanı boşaltın."
+ }
+ Progress = @{
+ MessageInstall = "Kurulum devam ediyor. Lütfen bekleyiniz..."
+ MessageInstallDetail = "Kurulum tamamlandığında bu pencere otomatik olarak kapanacaktır."
+ MessageRepair = "Onarım devam ediyor. Lütfen bekleyiniz..."
+ MessageRepairDetail = "Onarım tamamlandığında bu pencere otomatik olarak kapanacaktır."
+ MessageUninstall = "Kaldırma işlemi devam ediyor. Lütfen bekleyiniz..."
+ MessageUninstallDetail = "Kaldırma işlemi tamamlandığında bu pencere otomatik olarak kapanacaktır."
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "Simge durumuna küçült"
+ ButtonRestartNow = "Bilgisayarı yeniden başlat"
+ Message = "Yüklemenin tamamlanması için bilgisayarınızı yeniden başlatmanız gerekir."
+ MessageRestart = "Geri sayımın sonunda bilgisayarınız otomatik olarak yeniden başlatılacaktır."
+ MessageTime = "Lütfen çalışmanızı kaydedin ve belirtilen süre içinde yeniden başlatın."
+ TimeRemaining = "Kalan süre:"
+ Title = "Yeniden başlatma gerekmektedir"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0} otomatik olarak devam edecektir:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - Uygulama {0}'
+ DialogMessage = 'Aşağıdaki uygulamalar otomatik olarak kapatılacağından devam etmeden önce lütfen çalışmanızı kaydedin.'
+ DialogMessageNoProcesses = "Kuruluma devam etmek için lütfen Yükle'yi seçin. Kalan ertelemeleriniz varsa, kurulumu ertelemeyi de seçebilirsiniz."
+ ButtonDeferRemaining = 'kalır'
+ ButtonLeftText = 'Erteleme'
+ ButtonRightText = 'Uygulamaları Kapat ve Yükle'
+ ButtonRightTextNoProcesses = 'Kurulum'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC1SRpsYGC6lrrM
+# iOEseoQJsWLWqQg3KYYG/ylb8lww3qCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgAZrq57DQ8KtBHFOp8jDacJ6yCZtSsVp6/znfyanjEV0w
+# DQYJKoZIhvcNAQEBBQAEggGAJ+sqB5ScpW+xZ8KiLZ/UMc5QXhlNfPR5my1GDs4Y
+# m4hU+m1yXPSwXiSpOpxKBr21i25XCKUS/bznGjb4sysqsR6gRt/hTh0berzXip64
+# eV0aw6bPjrDyzNeejYbakSPVoUiNQqEYZPgG+Ia41NBu1Dds8rAX2EssNdnge1cM
+# bw7UevFHxE9AueNZtnbiYGLAihS4l6LD37l9pdatJj7f13i2aX0tXYwYWR/y3YeL
+# E47/i6olZ4Yjvt5RIyMuwz2karepaweroe0MMI0FQ0bW8SxbGMhmp5WXs42QA080
+# rJJ0hTthf3NUxcN+5Jzz8L5bNzxLaGFLfs+/mVOqGNZOv+5XhpLUt2sigM8HZ+rb
+# +r3bmZoaxvz8kPP2O5TrDFo/0fxrpEPNvSlmDa40K2qqcxQPr1jbJ55uTuFwju2U
+# J4JwvyHrefWR/x15DJyU76m5LTI43Kefi1KvX6rRLb1PKfmkxfTd9vB7HSxc5d7Q
+# R/BdObHiI8l8I4+G+1ZPFTXboYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIF/+lp3x
+# a5FSbffUK1QsKmCGogo6HK7tQFkzeUUi5DCUAhBtjOglpeNiN5eVSRSev4PiGA8y
+# MDI1MDIyMzIzNDgyNFqgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgyNFowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIN97+F0GYXkXencrW2UuSx1OY3+o23RKNcYRtD8d
+# xUsNMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICALtUiQw6Rm+KadJSbKTusuC5
+# 8D5Oz8rEcfprs3NVhuamNpxFDbeDvc2RH5rwLY1AOGyVetIC20qkUpTjVcgY5FFb
+# F5CGuIsldDKmaDLNjaK4CFYh7ut6meylgSU3m0qGEKOyz4Buea3FI/+rGcnYne5D
+# moUSD9o1joNrJWi5pYYfPByU1YmWcMtRf5MXIwlh8qh95p1Rg3iQXvv6paQBnUXw
+# EoX94vPK/1MYkgOvB7zZsTCZ5zKrcoZ4U8b/meR6JrU28KG2hZWwtsVpiFjWYvNs
+# UrbAfp6npftBua3mKPlhPnOXAmKsZVKOEOjhMwipHKH0MAsFNUeraTxYJ+vHtJgQ
+# NbY8QVkB01kEXGqDu+UWlDZyzLy1ARKNspMWCabzIMiHD0z/PI6n5HFHf980xZ/f
+# hOtmcpMIPleWeUF4VbHxiG7ffmqiZBuAFSTfdPL2m3/CPc7ccUPKUVlDzdLJk3Px
+# +VOhoTSZDkOBidua52roa0PdHGSKtXIMb3/wo/xygqQ/UXUGFAzRmw6QdKjEArYJ
+# l2pqdl5Gz6d/+yuibZBl09yO3Vu535g47cFnqqKUGdDDEs525scyqMzV3A0dHXGy
+# 7dYzMAUpnD02jE32FP9TjZnvWReaI3PcThZtHYkX698gyciCF2Kjs2znysnaoNkB
+# OmCjwe9XaovVBHp3s7Wx
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/zh-hans/strings.psd1 b/PSAppDeplyToolkit/Strings/zh-hans/strings.psd1
new file mode 100644
index 0000000..bec1e84
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/zh-hans/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "完成。"
+ Error = "失败。"
+ FastRetry = "未完成。"
+ RestartRequired = "完成。必须重新启动。"
+ Start = "已启动。"
+ }
+ BlockExecution = @{
+ Message = "为完成安装过程,暂时禁止启动这款应用程式。"
+ }
+ ClosePrompt = @{
+ ButtonClose = "关闭程序"
+ ButtonContinue = "继续"
+ ButtonContinueTooltip = "在关闭上列应用程式后才选择`"继续`"。"
+ ButtonDefer = "延迟"
+ CountdownMessage = "注:下列程序将自动关闭:"
+ Message = "为继续安装,必须关闭下列程序。`n`n请保存您的工作,关闭程序,然后继续。 或者保存您的工作,点击`"关闭程序`"。"
+ }
+ DeferPrompt = @{
+ Deadline = "最后期限:"
+ ExpiryMessage = "在延期失效前,可选择延迟安装:"
+ RemainingDeferrals = "所剩延期:"
+ WarningMessage = "延期失效后,再也无法延迟安装。"
+ WelcomeMessage = "即将安装下列应用程式:"
+ }
+ DeploymentType = @{
+ Install = "安装"
+ Repair = "修复"
+ Uninstall = "卸载"
+ }
+ DiskSpace = @{
+ Message = "没有足够的磁盘空间来完成下列安装:`n{0}`n`n所需空间:{1}MB`n可用空间:{2}MB`n`n请释放足够的磁盘空间以继续安装。"
+ }
+ Progress = @{
+ MessageInstall = "安装中。请稍等。。。"
+ MessageInstallDetail = "安装完成后,该窗口将自动关闭。。。"
+ MessageRepair = "修复中。请稍等。。。"
+ MessageRepairDetail = "修复完成后,该窗口将自动关闭。。。"
+ MessageUninstall = "卸载中。请稍等。。。"
+ MessageUninstallDetail = "卸载完成后,该窗口将自动关闭。。。"
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "最小化"
+ ButtonRestartNow = "现在重启"
+ Message = "为完成安装过程,需重启计算机。"
+ MessageRestart = "倒计时结束后,计算机将自动重启。"
+ MessageTime = "请保存您的工作,并在容许时间重启计算机。"
+ TimeRemaining = "剩余时间:"
+ Title = "需重启"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0}会自动继续:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - 应用程序 {0}'
+ DialogMessage = '请保存您的工作后再继续,因为以下应用程序将自动关闭。'
+ DialogMessageNoProcesses = '请选择 “安装 ”继续安装。如果您还有任何延迟,也可以选择延迟安装。'
+ ButtonDeferRemaining = '残留'
+ ButtonLeftText = '推迟'
+ ButtonRightText = '关闭应用程序并安装'
+ ButtonRightTextNoProcesses = '安装'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuKwYJKoZIhvcNAQcCoIIuHDCCLhgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCGWZ0t0SNE4NXr
+# 7YhaI6Zv1DrtIAXiihBIrZdKiE7yoKCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghnsMIIZ6AIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgVwLfWxvQ6ZayawpUTa1Ng5EbiwAL2pV2Jji6kEl4mTEw
+# DQYJKoZIhvcNAQEBBQAEggGAae8QkR9FE1wN8ERGCitp5Oe2YuWk9JiA7h0nXuYc
+# Uey4TYbodqP5LCzrviopxOBBlcubdqR9Mv2C5K+b6BnFj5ICRrSOtHpI2Pvma0QM
+# eHbGl3vxmPHKxwkmK/VpW63kbNKy4lFFYusjHQe7G5hzYIQFDduLazEeUynrRkEy
+# hUYMXY2Lpdf4D/VIL7mfYnwRld1nY/jMEWMzwdPuspIRTKChxquABRrsfGQrI1wp
+# 5xfTfVDNzwZiQTionukzIMHy/jTicCb9b3YWTnXEAbtZbRf4+DBXgDhhvLZit+w1
+# 28LQIGIJOwZ/jqfObUtXLJwFYQtipYJ89EQBr4J9SnAszi8LoTUS6bNAgxqD57eZ
+# zElG/APpcgseEXQ6fLARfyYYOzNuhvX+sv27ez78jadAA9ZAvdcomBJ6RUH36TNa
+# o8NFD/Xtg72prGGMzcvZTGefieOwU4k5JotZOCT6hp6s0MT2LUAynbZGUaEc2Rc9
+# lx2wlM3DOQ57kc74r3kG5yrNoYIXOTCCFzUGCisGAQQBgjcDAwExghclMIIXIQYJ
+# KoZIhvcNAQcCoIIXEjCCFw4CAQMxDzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0B
+# CRABBKBoBGYwZAIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIGvg2djp
+# 0HgkEk1lhUQ/0YVuQb3iSAASLBASzYo8IIV3AhBrveeAKpr8tDt/FX0Yw4GfGA8y
+# MDI1MDIyMzIzNDgyN1qgghMDMIIGvDCCBKSgAwIBAgIQC65mvFq6f5WHxvnpBOMz
+# BDANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
+# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT
+# SEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTI0MDkyNjAwMDAwMFoXDTM1MTEyNTIz
+# NTk1OVowQjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSAwHgYDVQQD
+# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBAL5qc5/2lSGrljC6W23mWaO16P2RHxjEiDtqmeOlwf0KMCBDEr4I
+# xHRGd7+L660x5XltSVhhK64zi9CeC9B6lUdXM0s71EOcRe8+CEJp+3R2O8oo76EO
+# 7o5tLuslxdr9Qq82aKcpA9O//X6QE+AcaU/byaCagLD/GLoUb35SfWHh43rOH3bp
+# LEx7pZ7avVnpUVmPvkxT8c2a2yC0WMp8hMu60tZR0ChaV76Nhnj37DEYTX9ReNZ8
+# hIOYe4jl7/r419CvEYVIrH6sN00yx49boUuumF9i2T8UuKGn9966fR5X6kgXj3o5
+# WHhHVO+NBikDO0mlUh902wS/Eeh8F/UFaRp1z5SnROHwSJ+QQRZ1fisD8UTVDSup
+# WJNstVkiqLq+ISTdEjJKGjVfIcsgA4l9cbk8Smlzddh4EfvFrpVNnes4c16Jidj5
+# XiPVdsn5n10jxmGpxoMc6iPkoaDhi6JjHd5ibfdp5uzIXp4P0wXkgNs+CO/CacBq
+# U0R4k+8h6gYldp4FCMgrXdKWfM4N0u25OEAuEa3JyidxW48jwBqIJqImd93NRxvd
+# 1aepSeNeREXAu2xUDEW8aqzFQDYmr9ZONuc2MhTMizchNULpUEoA6Vva7b1XCB+1
+# rxvbKmLqfY/M/SdV6mwWTyeVy5Z/JkvMFpnQy5wR14GJcv6dQ4aEKOX5AgMBAAGj
+# ggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8E
+# DDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEw
+# HwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFJ9XLAN3
+# DigVkGalY17uT5IfdqBbMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRp
+# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3Rh
+# bXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRw
+# Oi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRz
+# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1l
+# U3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAD2tHh92mVvjOIQSR9lD
+# kfYR25tOCB3RKE/P09x7gUsmXqt40ouRl3lj+8QioVYq3igpwrPvBmZdrlWBb0Hv
+# qT00nFSXgmUrDKNSQqGTdpjHsPy+LaalTW0qVjvUBhcHzBMutB6HzeledbDCzFzU
+# y34VarPnvIWrqVogK0qM8gJhh/+qDEAIdO/KkYesLyTVOoJ4eTq7gj9UFAL1UruJ
+# KlTnCVaM2UeUUW/8z3fvjxhN6hdT98Vr2FYlCS7Mbb4Hv5swO+aAXxWUm3WpByXt
+# gVQxiBlTVYzqfLDbe9PpBKDBfk+rabTFDZXoUke7zPgtd7/fvWTlCs30VAGEsshJ
+# mLbJ6ZbQ/xll/HjO9JbNVekBv2Tgem+mLptR7yIrpaidRJXrI+UzB6vAlk/8a1u7
+# cIqV0yef4uaZFORNekUgQHTqddmsPCEIYQP7xGxZBIhdmm4bhYsVA6G2WgNFYagL
+# DBzpmk9104WQzYuVNsxyoVLObhx3RugaEGru+SojW4dHPoWrUhftNpFC5H7QEY7M
+# hKRyrBe7ucykW7eaCuWBsBb4HOKRFVDcrZgdwaSIqMDiCLg4D+TPVgKx2EgEdeoH
+# NHT9l3ZDBD+XgbF+23/zBjeCtxz+dL/9NWR6P2eZRi7zcEO1xwcdcqJsyz/JceEN
+# c2Sg8h3KeFUCS7tpFk7CrDqkMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipe
+# WzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl
+# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdp
+# Q2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1
+# OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5
+# BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0
+# YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1Bkmz
+# wT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkL
+# f50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C
+# 3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5
+# n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUd
+# zTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWH
+# po9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/
+# oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPV
+# A+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg
+# 0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mM
+# DDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6E
+# VO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB
+# /wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1Ud
+# IwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNV
+# HSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0
+# dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2Vy
+# dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0f
+# BDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
+# c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBT
+# zr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/E
+# UExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fm
+# niye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szw
+# cqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8TH
+# wcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/
+# JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9
+# Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm
+# 228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVB
+# tzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnw
+# ZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv2
+# 7dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEM
+# BQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE
+# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJ
+# RCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkG
+# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIIC
+# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zC
+# pyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf
+# 1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x
+# 4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEio
+# ZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7ax
+# xLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZ
+# OjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJ
+# l2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz
+# 2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH
+# 4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb
+# 5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ
+# 9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYD
+# VR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuC
+# MS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
+# dENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5j
+# b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0g
+# ADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs
+# 7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq
+# 3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/
+# Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9
+# /HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWoj
+# ayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3YwggNyAgEB
+# MHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYD
+# VQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFt
+# cGluZyBDQQIQC65mvFq6f5WHxvnpBOMzBDANBglghkgBZQMEAgEFAKCB0TAaBgkq
+# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI1MDIyMzIz
+# NDgyN1owKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU29OF7mLb0j575PZxSFCHJNWG
+# W0UwLwYJKoZIhvcNAQkEMSIEIG4T8R1T71AEu9tG4H0GrOum5D1MlmIPYw7JdLDN
+# FgV0MDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIHZ2n6jyYy8fQws6IzCu1lZ1/tdz
+# 2wXWZbkFk5hDj5rbMA0GCSqGSIb3DQEBAQUABIICADbBFUo1E86v88Pdpum3Aq5T
+# eH3SzlK/mnOMhpSNnpdIyxKJqRe1zBUJFI1vz3dkP1FORPMhx7pZRilxKFxIwg3E
+# 5rh5LQo19CEufb2KmQXveLtUvlWFGs5YjPREq7c7v+usPCc6JSrx8ruYXBBbu1co
+# zcGQnr5yxqc+ygmZBCsWrOPJ9MLbIiiwli4cj4uqu3/PZlOIlspp8bx/u0nSlhW8
+# q2mUwDhby6D086OI6u2uq2R+p5i0qihIMp6VadVplhLjpyTxV3LUr+cUmPiZH/dc
+# COFNtZUFwlxigkVSFKWwPyo8BseKG1FkHLjvs0uxASu72YX7FRrY7OX4W3PkfBWp
+# rSJr4kb/FG5sgCRRW7tTIuLZIyKYPMhXOcnpL//Q21dg8aMX9Bw+2aCnO+CwhDpH
+# 6HbcspiR4eN6esu8mat0Ks5RP8k6ojkkj+9bvOwcG280tW6pIEPfP1cFSNR+AVoq
+# KWuE35zJRFkG8X0LNtIQLxcxoVUjRr1IrnADVhbSEYgtsXH718rkLJIJ/q3aGC1D
+# jDrhc3uf/KKsMzQPgxtn32gBKRE6zr0I0/W8ulcLcwQJA8XdEvxtdITen6jDOetK
+# UCYOg4XzC2F8luV4JSLasxnuQBM5sjRkhzdqVhEA4mqdLUtCP28TLYvwmPdkpcGy
+# CScQdVv5OqMQOvPC9n2G
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Strings/zh-hant/strings.psd1 b/PSAppDeplyToolkit/Strings/zh-hant/strings.psd1
new file mode 100644
index 0000000..52e94b3
--- /dev/null
+++ b/PSAppDeplyToolkit/Strings/zh-hant/strings.psd1
@@ -0,0 +1,317 @@
+@{
+ BalloonText = @{
+ Complete = "完成。"
+ Error = "失敗。"
+ FastRetry = "未完成。"
+ RestartRequired = "完成。需重啟。"
+ Start = "已啟動。"
+ }
+ BlockExecution = @{
+ Message = "為完成安裝過程,暫時禁止啟動本款應用程式。"
+ }
+ ClosePrompt = @{
+ ButtonClose = "關閉程序"
+ ButtonContinue = "繼續"
+ ButtonContinueTooltip = "關閉上列應用程式後才選擇`"繼續`"。"
+ ButtonDefer = "延遲"
+ CountdownMessage = "注:下列程序將自動關閉:"
+ Message = "在繼續安裝前必須關閉下列程序。`n`n請保存您的工作,關閉程序,然後繼續。 或者保存您的工作,然後點擊`"關閉程序`"。"
+ }
+ DeferPrompt = @{
+ Deadline = "最後期限:"
+ ExpiryMessage = "在延期失效前,可選擇延遲安裝:"
+ RemainingDeferrals = "所剩延期:"
+ WarningMessage = "延期失效後,再也無法延遲安裝。"
+ WelcomeMessage = "即將安裝下列應用程式:"
+ }
+ DeploymentType = @{
+ Install = "安裝"
+ Repair = "修復"
+ Uninstall = "卸載"
+ }
+ DiskSpace = @{
+ Message = "沒有足夠的磁盤空間來完成下列安裝:`n{0}`n`n所需空間: {1}MB`n可用空間: {2}MB`n`n請釋放足夠的磁盤空間以繼續安裝。"
+ }
+ Progress = @{
+ MessageInstall = "安裝中。請稍等。。。"
+ MessageInstallDetail = "安裝完成後,此視窗會自動關閉。。。"
+ MessageRepair = "修復中。請稍等。。。"
+ MessageRepairDetail = "修復完成後,此視窗會自動關閉。。。"
+ MessageUninstall = "卸載中。請稍等。。。"
+ MessageUninstallDetail = "卸載完成後,此視窗將自動關閉。。。"
+ }
+ RestartPrompt = @{
+ ButtonRestartLater = "最小化"
+ ButtonRestartNow = "現在重啟"
+ Message = "未完成安裝過程,需重啟計算機。"
+ MessageRestart = "倒計時結束後,計算機將自動重啟。"
+ MessageTime = "請保存您的工作,然後在容許時間重啟計算機。"
+ TimeRemaining = "剩餘時間:"
+ Title = "需重啟"
+ }
+ WelcomePrompt = @{
+ Classic = @{
+ CountdownMessage = "{0}會自動繼續:"
+ CustomMessage = ""
+ }
+ Fluent = @{
+ Subtitle = 'PSAppDeployToolkit - 應用程式 {0}'
+ DialogMessage = '由於下列應用程式將自動關閉,請先保存您的工作再繼續。'
+ DialogMessageNoProcesses = '請選擇「安裝」繼續安裝。如果您有任何延遲安裝的剩餘時間,您也可以選擇延遲安裝。'
+ ButtonDeferRemaining = '留下'
+ ButtonLeftText = '延遲'
+ ButtonRightText = '關閉應用程式並安裝'
+ ButtonRightTextNoProcesses = '安裝'
+ }
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAgFrkLudTcZvlG
+# I5x/vJJrpiouJpDNPhR0B8DOKPtkbKCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgEW2orimYZqVqXZEB2LefSa6lncVGcsygu57kD68ffY4w
+# DQYJKoZIhvcNAQEBBQAEggGAWZX/k6Ao+VcZgqEUyYpudPSPOvVHJwU6elIQ0D2t
+# jzD5/NSAUoW4PibXdcJbfBSAyIPcX9CWvbLShjBMhHOuDysEyOtQUw0YCbL2uISc
+# ojKAAiqZeGP8fC5/RIx4c0ZM3DBYwm6H1QHCK6EIeb3PtwI5cMBxca2DbvKdPrAb
+# A3X1Dz5NPUUusl4xFiisQyzSrMWPeMcO6Y0IeCkBHKk8o30RrgH3v8S5p900ExzT
+# RwAe+ywsGPNMa/DcYGOgU/L2bg6WO0rPQzqs+C35JGXtbPWFofQQZ4LeSKdqc7vI
+# FlkeSn6RqiOj95j6MO/g7SemzjZEDJwQ2qhs567EshlUcoTuY++IYgGdTpJLgl1Y
+# q4/FEsl+AWmsiukdgVqFmvylivORONkxxWE0ag0QWu8PoKwwpWOjx/r7v9lWlYoh
+# JtqvvO84fulzOPEB3Levxh0H9lwPEmfqcO7ZiAvDGFD1G6YHZdrhG1LX+WXtqBpE
+# 1cHNTwkXMAg0H+XoWs+5JdxQoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEICzTo6wn
+# GMsRaV5X+wT2YhPqEV1qp4E275ZE7cW+Xkq8AhEA+RefDEb++8LCjgtt9DXqJRgP
+# MjAyNTAyMjMyMzQ4MjlaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ4MjlaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCBcp6yGduR+W/i0dIOQ/2tSMpTdhmmuQQjTeRxx
+# kdtc9DA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgAuQCmpRQ29kPHjuk+L7AU2
+# 91xoVabAoARvON/0HtwfVrWOdsFzlPSg9sx0/XbxRdJKeG3rupnujrayNlRC2Wbg
+# WvJwwAMwjMxeJxSJNZggyHqxSy90wLQFbGmNDgCYoYfngPkpQdY2IBWrHSmRXLv5
+# I8pXns9+Bqh46CawoKhZ7XwhO9xNdT4+cBQQG/a+blbSxawxfkKkT2cvUI+QsOdD
+# cafSCV7MZ6w3mtYPDnsne4l8xoNBGY2dRqH3pJZX9SASbMD4fzltTFitV223z9fD
+# Itl/3w7LP2Cz7SMZfljOrT+uXsjo4smdTEwWmAuI8L2qtkLMsX25L+7uO6ZSlVo1
+# cJlzbw4UWpUK8e+6f2HZzr7pPAhxObYWGCjBK3CQNB9rT4JRrRmQCPlAhosbDLhM
+# ur+FGCqbfNWXYZ67Bw6lM11npfEy1go5c1IcBJeGYS6IpeiUnd6t9xN95MbaFyJB
+# PQFQkynxiIOUzd2FXqHeVwWLBtSNM7ZEGH4LrwVb0hi94JKe3GA9tzA1iDGbIlUs
+# Z/mRqG4B4Zi/TpyprugHTcu05SnqqKrEZq+nUpUbvW7VUiNkGBk0512yzT8UxUdI
+# QD7o2e29U0xJrkKGaQhKcmPn3GuMmFDnBO3UhMva1h1Yu9tT0ASTXSCMqH0fRlnC
+# 4WDdq5AGQJsvCcL8f4RwIQ==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/Templates/New-ADTFunction.ps1 b/PSAppDeplyToolkit/Templates/New-ADTFunction.ps1
new file mode 100644
index 0000000..930f7f8
--- /dev/null
+++ b/PSAppDeplyToolkit/Templates/New-ADTFunction.ps1
@@ -0,0 +1,329 @@
+#-----------------------------------------------------------------------------
+#
+# MARK: New-ADTFunction
+#
+#-----------------------------------------------------------------------------
+
+function New-ADTFunction
+{
+ <#
+ .SYNOPSIS
+ Basis for a new PSAppDeployToolkit function.
+
+ .DESCRIPTION
+ This function serves as the basis for a new PSAppDeployToolkit function.
+
+ .INPUTS
+ None
+
+ You cannot pipe objects to this function.
+
+ .OUTPUTS
+ None
+
+ This function does not return any output.
+
+ .EXAMPLE
+ New-ADTFunction
+
+ Invokes the New-ADTFunction function and returns any output.
+
+ .NOTES
+ An active ADT session is NOT required to use this function.
+
+ Tags: psadt
+ Website: https://psappdeploytoolkit.com
+ Copyright: (C) 2025 PSAppDeployToolkit Team (Sean Lillis, Dan Cunningham, Muhammad Mashwani, Mitch Richters, Dan Gough).
+ License: https://opensource.org/license/lgpl-3-0
+
+ .LINK
+ https://psappdeploytoolkit.com
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ )
+
+ begin
+ {
+ # Initialize function.
+ Initialize-ADTFunction -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
+ }
+
+ process
+ {
+ try
+ {
+ try
+ {
+ }
+ catch
+ {
+ # Re-writing the ErrorRecord with Write-Error ensures the correct PositionMessage is used.
+ Write-Error -ErrorRecord $_
+ }
+ }
+ catch
+ {
+ # Process the caught error, log it and throw depending on the specified ErrorAction.
+ Invoke-ADTFunctionErrorHandler -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState -ErrorRecord $_
+ }
+ }
+
+ end
+ {
+ # Finalize function.
+ Complete-ADTFunction -Cmdlet $PSCmdlet
+ }
+}
+
+# SIG # Begin signature block
+# MIIuLAYJKoZIhvcNAQcCoIIuHTCCLhkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDUTO8v9Qk1WojG
+# +K72mr256qC4pioNyHrxPDZvqGltGKCCE5UwggWQMIIDeKADAgECAhAFmxtXno4h
+# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
+# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
+# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
+# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
+# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
+# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
+# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
+# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
+# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
+# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
+# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
+# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
+# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
+# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
+# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
+# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
+# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
+# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
+# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
+# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
+# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
+# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
+# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
+# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
+# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
+# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
+# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
+# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
+# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
+# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
+# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
+# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
+# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
+# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
+# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
+# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
+# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
+# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
+# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
+# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
+# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
+# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
+# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
+# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
+# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
+# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
+# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
+# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
+# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
+# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
+# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
+# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
+# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
+# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
+# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
+# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
+# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
+# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
+# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
+# eE4wggdJMIIFMaADAgECAhAK+Vu2vqIMhQ6YxvuOrAj5MA0GCSqGSIb3DQEBCwUA
+# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
+# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
+# ODQgMjAyMSBDQTEwHhcNMjQwOTA1MDAwMDAwWhcNMjcwOTA3MjM1OTU5WjCB0TET
+# MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhDb2xvcmFkbzEd
+# MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xFDASBgNVBAUTCzIwMTMxNjM4
+# MzI3MQswCQYDVQQGEwJVUzERMA8GA1UECBMIQ29sb3JhZG8xFDASBgNVBAcTC0Nh
+# c3RsZSBSb2NrMRkwFwYDVQQKExBQYXRjaCBNeSBQQywgTExDMRkwFwYDVQQDExBQ
+# YXRjaCBNeSBQQywgTExDMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA
+# uydxko2Hrl6sANJUjfdypKP60qBH5EkhfaRQAnn+e3vg2eVcbiEWIjlrMYzvK2sg
+# OMBbwGebqAURkFmUCKDdGxcxKeuXdaXPHWPKwc2WjYCFajrX6HofiiwNzOCdL6VE
+# 4PDQhPRR7SIdNNFSrx5C4ZDN1T6OH+ydX7EQF8+NBUNHRbEVdl+h9H5Aexx63afa
+# 8zu3g/GXluyXKbb+JHtgNJaUgFuFORTxw1TO6qH+S6Hrppf9QcAFmu4xGtkc2FSh
+# gv0NgWMNGDZqJr/o9sqJ2tdaZHDyr6H8PvY8egoUshF7ccgEYtEEdB9SRR8mVQik
+# 1w5oGTjDWjHj+8jgTpzletRywptk/m8PehVBN8ntqoSdvLLcuQVzmuPLzN/iuKh5
+# sZeWvqPONApcEnZcONpXebyiUPnEePr5rZAU7hMjMw2ZPnQlMcbGvtgP2qi7m2f3
+# mXFYxWjlKCxaApYHeqSFeWC8zM7OYL2HlZ+GuK4XG8jKVE6sWSW9Wk/dm0vJbasv
+# AgMBAAGjggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAd
+# BgNVHQ4EFgQU5GCU3SEqeIbhhY9eyU0LcTI75X8wPQYDVR0gBDYwNDAyBgVngQwB
+# AzApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYD
+# VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaow
+# U6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
+# NENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRw
+# Oi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
+# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYI
+# KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQ
+# aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
+# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkq
+# hkiG9w0BAQsFAAOCAgEAqA0ub/ilMgdIvMiBeWBoiMxe5OIblObGI7lemcP2WEqa
+# EASW11/wVwJU63ZwhtkQaNU4rXjf6fqy5pOUzpQXgYjSaO4D/AOMJKHlypxslFqZ
+# /dYpcue2xE3H7lmO4KPf8VxXuFIUqjLetU+kkh7o/Q52RabVAuOrPFKnObixy1HI
+# x0/5F+RuP9xhqmDbfM7l5zUAcuOCCkY7buuInEsip9BZXUiVb8K5bPR9Rk7Doat4
+# FQmN72xjakcEZOMU/vg0ZgVa8nxkBXtVsjxbsr+bODn0cddHK1QHWil/PmpANkxN
+# 7H8tdCAZ8bTzIvvudxSLnt7ssbbQDkAyNw0btDH+MKv/l+VcYyQH51Z5xT9DvHCm
+# Ed774boZkP2GfTFvn7/gISEjTdOuUGstdrgSwg1zJPqgK7zWxK48xC7awpa3gwOs
+# 9pnyiqHG3rx84/SHUiAL2lkljsD3epmRxsWeZhZNY93xEpQHe9LBvo/t4VRjZzqU
+# z+pfEMPqeX/g5+mpb4ap6ZmNJuAYJFmU0LIkCLQN9mKXi1Il9WU6ifn3vYutGMSL
+# /BdeWP+7fM7MZLiO+1BIsBdSmV6pZVS3LRBAy3wIlbWL69mvyLCPIQ7z4dtfuzwC
+# 36E9k2vhzeiDQ+k1dFJDSdxTDetsck0FuD1ovhiu2caL4BdFsCWsXPLMyvu6OlYx
+# ghntMIIZ6QIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
+# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
+# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAr5W7a+ogyFDpjG+46sCPkwDQYJYIZI
+# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
+# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
+# BgkqhkiG9w0BCQQxIgQgLeBNCFlHvWeoJbPTled3xiTzUCCWyEW5E+JoLhUUiZMw
+# DQYJKoZIhvcNAQEBBQAEggGATDJmWs6mScURoUv6Gq2Jz6atSXQ0prRa5IQiJdNz
+# tLaxIOOb49ireGZIvEXqOhu7cmcE0vdArdhC55n0kUbXAuYWdHZFQDhM++j068xt
+# VHfvGCKp7hnRTxbokT+6F67cjvl3qVSUzcsta1ruQqZrKjziw/v+hmsa1sac02im
+# ONDg75jWeKetbwCqS5uIAI3mydtY9XyL4dYfpKfhH4+xEJaG6AYHpO37V214UV2a
+# IF+DTgyxjFUR5AmhXCMtQUr9y98v31o6wJkm0ozxLPU4GFdheo51cPUA6lZzWjpS
+# T1tImq6GWKx9OrkVOEwl1gbv7IPcsynR5WgTaaSy2nt7hIb9uhq5yRd2SZ0GcW9U
+# NkBud7gn02wfM+Z/xaF3rZN1k5HAdyUeQGMoD+SEStIxCJnjb7k67Jg9yNW2YQBX
+# QQh3r0MEiGHYVJRnuuJ23uSXrUP+uNSx6XPcnN6xBGcJ5XxWSgnr0BikxuM6ueXb
+# jrXMxtYmECkJ/c/DANYAXvgAoYIXOjCCFzYGCisGAQQBgjcDAwExghcmMIIXIgYJ
+# KoZIhvcNAQcCoIIXEzCCFw8CAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG9w0B
+# CRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEILQnxvvY
+# 1hsJxke0YOLMMCbD0gZRJA7bIgw+Klh9r4UzAhEAgeA0yXFcCTGepwD9RkJgMxgP
+# MjAyNTAyMjMyMzQ4MzVaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTj
+# MwQwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
+# ZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYg
+# U0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUy
+# MzU5NTlaMEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UE
+# AxMXRGlnaUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+# DwAwggIKAoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+
+# CMR0Rne/i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+h
+# Du6ObS7rJcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh92
+# 6SxMe6We2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjW
+# fISDmHuI5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496
+# OVh4R1TvjQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0r
+# qViTbLVZIqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY
+# +V4j1XbJ+Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnA
+# alNEeJPvIeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb
+# 3dWnqUnjXkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgf
+# ta8b2ypi6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQAB
+# o4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/
+# BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcB
+# MB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywD
+# dw4oFZBmpWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5k
+# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0
+# YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0
+# cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0
+# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGlt
+# ZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZ
+# Q5H2EdubTggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B
+# 76k9NJxUl4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc
+# 1Mt+FWqz57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7
+# iSpU5wlWjNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl
+# 7YFUMYgZU1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLI
+# SZi2yemW0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtb
+# u3CKldMnn+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGo
+# Cwwc6ZpPddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGO
+# zISkcqwXu7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXq
+# BzR0/Zd2QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHh
+# DXNkoPIdynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYq
+# XlswDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD
+# ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln
+# aUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIz
+# NTk1OVowYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTsw
+# OQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVT
+# dGFtcGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJ
+# s8E9cklRVcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJ
+# C3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+
+# QtxnjupRPfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3
+# eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbF
+# Hc02DVzV5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71
+# h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseS
+# v6De4z6ic/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj
+# 1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2L
+# INIsVzV5K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJ
+# jAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAO
+# hFTuzuldyF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMB
+# Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNV
+# HSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYD
+# VR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhho
+# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNl
+# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1Ud
+# HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy
+# dXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwH
+# ATANBgkqhkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88w
+# U86/GPvHUF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZv
+# xFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+R
+# Zp4snuCKrOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM
+# 8HKjI/rAJ4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/E
+# x8HBanHZxhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd
+# /yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFP
+# vT87eK1MrfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHics
+# JttvFXseGYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2V
+# Qbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ
+# 8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr
+# 9u3WfPwwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEB
+# DAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNV
+# BAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQg
+# SUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJ
+# BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k
+# aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCC
+# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPs
+# wqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLk
+# X9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDtt
+# ceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hI
+# qGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2
+# scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm
+# 2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaH
+# iZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3r
+# M9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJ
+# B+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRES
+# W+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kxfgom
+# mfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0G
+# A1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLL
+# gjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQG
+# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
+# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
+# b3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQu
+# Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUd
+# IAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGH
+# LOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7Q
+# Kt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajw
+# vy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQ
+# Pfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFq
+# I2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIB
+# ATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkG
+# A1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3Rh
+# bXBpbmcgQ0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJ
+# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAyMjMy
+# MzQ4MzVaMCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTV
+# hltFMC8GCSqGSIb3DQEJBDEiBCAeevbtTJWoclGv+SU0l2C3h6mlnLtTM1CG+wgK
+# 4GIp3TA3BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7X
+# c9sF1mW5BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgAzu5hjpRq50cHZjg25jyLc
+# ww+LWMZ4KOocPhmDdfU+7/KaAVRWBxFgALcG+qSnE4PAKTLyS0iNkzFR7yT8MZtb
+# OEc5Ka9la+tWDHYayvBBxBKJpUpDTaf/KLEojvdQ7SZJrgoi5toQyUyarJCjBj6l
+# 6kcmpb8BJMzPXv5AqsD6xSS23x1hwkIgImMpxGojmXyAUgP+Cz1pmBkDOPYN1eVZ
+# /naV11G0f+K7vY1+ORMPM4QRwTvTy0HEjCExKP2mDiJ+jVV+jPxW8aVHX3RyKJPj
+# oXELjPNciBrGtDfrUBa2tCYlv4fTUrJUonb226j20NOqb2O+v9fHG97QFUBWZW0V
+# R4CkIKwx4V2IKgjH9sJFeseEt10Yy8/vCOeA5ec83+BYHdYw9wMJmko8M7z3BVjW
+# EjQnGDlq6u+G3n+E9QksE7PCwmVbTU46kY2G6HOH8TZ+UD54+DF8qTqjQWpHCx75
+# Us8QNQzjycsstfoHy3YUXeU9eE+r3qQxicMzxcA4IVoDuqd+otrJkBHSeIMWCsRu
+# MX9GKtuewpVGp/u27HvLU16z797C7aCMzFpzIfmSFOBMgF6ycv4Z2wlKjLWkU9Z8
+# XLNCz508jaTlcuzNK4pUPMfOPs+83f/zIzvNel+AInGI8Le9eRPgFwAPfePUP/8N
+# FCgJGrqGGj6bN8hhWhlgtQ==
+# SIG # End signature block
diff --git a/PSAppDeplyToolkit/bin/AMD64/handle/Eula.txt b/PSAppDeplyToolkit/bin/AMD64/handle/Eula.txt
new file mode 100644
index 0000000..8efa711
--- /dev/null
+++ b/PSAppDeplyToolkit/bin/AMD64/handle/Eula.txt
@@ -0,0 +1,75 @@
+Sysinternals Software License Terms
+These license terms are an agreement between Sysinternals (a wholly owned subsidiary of Microsoft Corporation) and you. Please read them. They apply to the software you are downloading from technet.microsoft.com/sysinternals, which includes the media on which you received it, if any. The terms also apply to any Sysinternals
+* updates,
+* supplements,
+* Internet-based services,
+* and support services
+for this software, unless other terms accompany those items. If so, those terms apply.
+BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE.
+If you comply with these license terms, you have the rights below.
+
+Installation and User Rights
+
+You may install and use any number of copies of the software on your devices.
+
+Scope of License
+
+The software is licensed, not sold. This agreement only gives you some rights to use the software. Sysinternals reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not
+* work around any technical limitations in the software;
+* reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;
+* make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;
+* publish the software for others to copy;
+* rent, lease or lend the software;
+* transfer the software or this agreement to any third party; or
+* use the software for commercial software hosting services.
+
+Sensitive Information
+
+Please be aware that, similar to other debug tools that capture “process state” information, files saved by Sysinternals tools may include personally identifiable or other sensitive information (such as usernames, passwords, paths to files accessed, and paths to registry accessed). By using this software, you acknowledge that you are aware of this and take sole responsibility for any personally identifiable or other sensitive information provided to Microsoft or any other party through your use of the software.
+
+Documentation
+
+Any person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes.
+
+Export Restrictions
+
+The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting .
+
+Support Services
+
+Because this software is "as is," we may not provide support services for it.
+
+Entire Agreement
+
+This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.
+
+Applicable Law
+
+United States . If you acquired the software in the United States , Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.
+Outside the United States . If you acquired the software in any other country, the laws of that country apply.
+
+Legal Effect
+
+This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.
+
+Disclaimer of Warranty
+
+The software is licensed "as-is." You bear the risk of using it. Sysinternals gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this agreement cannot change. To the extent permitted under your local laws, sysinternals excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
+
+Limitation on and Exclusion of Remedies and Damages
+
+You can recover from sysinternals and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.
+This limitation applies to
+* anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and
+* claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.
+
+It also applies even if Sysinternals knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.
+Please note: As this software is distributed in Quebec , Canada , some of the clauses in this agreement are provided below in French.
+Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.
+EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Sysinternals n'accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection dues consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d'adéquation à un usage particulier et d'absence de contrefaçon sont exclues.
+LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Sysinternals et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
+Cette limitation concerne :
+tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et
+les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d'une autre faute dans la limite autorisée par la loi en vigueur.
+Elle s'applique également, même si Sysinternals connaissait ou devrait connaître l'éventualité d'un tel dommage. Si votre pays n'autorise pas l'exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l'exclusion ci-dessus ne s'appliquera pas à votre égard.
+EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d'autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
diff --git a/PSAppDeplyToolkit/bin/AMD64/handle/handle.exe b/PSAppDeplyToolkit/bin/AMD64/handle/handle.exe
new file mode 100644
index 0000000..c822dc9
Binary files /dev/null and b/PSAppDeplyToolkit/bin/AMD64/handle/handle.exe differ
diff --git a/PSAppDeplyToolkit/bin/ARM64/handle/Eula.txt b/PSAppDeplyToolkit/bin/ARM64/handle/Eula.txt
new file mode 100644
index 0000000..8efa711
--- /dev/null
+++ b/PSAppDeplyToolkit/bin/ARM64/handle/Eula.txt
@@ -0,0 +1,75 @@
+Sysinternals Software License Terms
+These license terms are an agreement between Sysinternals (a wholly owned subsidiary of Microsoft Corporation) and you. Please read them. They apply to the software you are downloading from technet.microsoft.com/sysinternals, which includes the media on which you received it, if any. The terms also apply to any Sysinternals
+* updates,
+* supplements,
+* Internet-based services,
+* and support services
+for this software, unless other terms accompany those items. If so, those terms apply.
+BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE.
+If you comply with these license terms, you have the rights below.
+
+Installation and User Rights
+
+You may install and use any number of copies of the software on your devices.
+
+Scope of License
+
+The software is licensed, not sold. This agreement only gives you some rights to use the software. Sysinternals reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not
+* work around any technical limitations in the software;
+* reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;
+* make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;
+* publish the software for others to copy;
+* rent, lease or lend the software;
+* transfer the software or this agreement to any third party; or
+* use the software for commercial software hosting services.
+
+Sensitive Information
+
+Please be aware that, similar to other debug tools that capture “process state” information, files saved by Sysinternals tools may include personally identifiable or other sensitive information (such as usernames, passwords, paths to files accessed, and paths to registry accessed). By using this software, you acknowledge that you are aware of this and take sole responsibility for any personally identifiable or other sensitive information provided to Microsoft or any other party through your use of the software.
+
+Documentation
+
+Any person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes.
+
+Export Restrictions
+
+The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting .
+
+Support Services
+
+Because this software is "as is," we may not provide support services for it.
+
+Entire Agreement
+
+This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.
+
+Applicable Law
+
+United States . If you acquired the software in the United States , Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.
+Outside the United States . If you acquired the software in any other country, the laws of that country apply.
+
+Legal Effect
+
+This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.
+
+Disclaimer of Warranty
+
+The software is licensed "as-is." You bear the risk of using it. Sysinternals gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this agreement cannot change. To the extent permitted under your local laws, sysinternals excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
+
+Limitation on and Exclusion of Remedies and Damages
+
+You can recover from sysinternals and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.
+This limitation applies to
+* anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and
+* claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.
+
+It also applies even if Sysinternals knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.
+Please note: As this software is distributed in Quebec , Canada , some of the clauses in this agreement are provided below in French.
+Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.
+EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Sysinternals n'accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection dues consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d'adéquation à un usage particulier et d'absence de contrefaçon sont exclues.
+LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Sysinternals et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
+Cette limitation concerne :
+tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et
+les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d'une autre faute dans la limite autorisée par la loi en vigueur.
+Elle s'applique également, même si Sysinternals connaissait ou devrait connaître l'éventualité d'un tel dommage. Si votre pays n'autorise pas l'exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l'exclusion ci-dessus ne s'appliquera pas à votre égard.
+EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d'autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
diff --git a/PSAppDeplyToolkit/bin/ARM64/handle/handle.exe b/PSAppDeplyToolkit/bin/ARM64/handle/handle.exe
new file mode 100644
index 0000000..661e24c
Binary files /dev/null and b/PSAppDeplyToolkit/bin/ARM64/handle/handle.exe differ
diff --git a/PSAppDeplyToolkit/bin/i386/handle/Eula.txt b/PSAppDeplyToolkit/bin/i386/handle/Eula.txt
new file mode 100644
index 0000000..8efa711
--- /dev/null
+++ b/PSAppDeplyToolkit/bin/i386/handle/Eula.txt
@@ -0,0 +1,75 @@
+Sysinternals Software License Terms
+These license terms are an agreement between Sysinternals (a wholly owned subsidiary of Microsoft Corporation) and you. Please read them. They apply to the software you are downloading from technet.microsoft.com/sysinternals, which includes the media on which you received it, if any. The terms also apply to any Sysinternals
+* updates,
+* supplements,
+* Internet-based services,
+* and support services
+for this software, unless other terms accompany those items. If so, those terms apply.
+BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE.
+If you comply with these license terms, you have the rights below.
+
+Installation and User Rights
+
+You may install and use any number of copies of the software on your devices.
+
+Scope of License
+
+The software is licensed, not sold. This agreement only gives you some rights to use the software. Sysinternals reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not
+* work around any technical limitations in the software;
+* reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation;
+* make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation;
+* publish the software for others to copy;
+* rent, lease or lend the software;
+* transfer the software or this agreement to any third party; or
+* use the software for commercial software hosting services.
+
+Sensitive Information
+
+Please be aware that, similar to other debug tools that capture “process state” information, files saved by Sysinternals tools may include personally identifiable or other sensitive information (such as usernames, passwords, paths to files accessed, and paths to registry accessed). By using this software, you acknowledge that you are aware of this and take sole responsibility for any personally identifiable or other sensitive information provided to Microsoft or any other party through your use of the software.
+
+Documentation
+
+Any person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes.
+
+Export Restrictions
+
+The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see www.microsoft.com/exporting .
+
+Support Services
+
+Because this software is "as is," we may not provide support services for it.
+
+Entire Agreement
+
+This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services.
+
+Applicable Law
+
+United States . If you acquired the software in the United States , Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.
+Outside the United States . If you acquired the software in any other country, the laws of that country apply.
+
+Legal Effect
+
+This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.
+
+Disclaimer of Warranty
+
+The software is licensed "as-is." You bear the risk of using it. Sysinternals gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this agreement cannot change. To the extent permitted under your local laws, sysinternals excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
+
+Limitation on and Exclusion of Remedies and Damages
+
+You can recover from sysinternals and its suppliers only direct damages up to U.S. $5.00. You cannot recover any other damages, including consequential, lost profits, special, indirect or incidental damages.
+This limitation applies to
+* anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and
+* claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law.
+
+It also applies even if Sysinternals knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages.
+Please note: As this software is distributed in Quebec , Canada , some of the clauses in this agreement are provided below in French.
+Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français.
+EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Sysinternals n'accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection dues consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d'adéquation à un usage particulier et d'absence de contrefaçon sont exclues.
+LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Sysinternals et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices.
+Cette limitation concerne :
+tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et
+les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d'une autre faute dans la limite autorisée par la loi en vigueur.
+Elle s'applique également, même si Sysinternals connaissait ou devrait connaître l'éventualité d'un tel dommage. Si votre pays n'autorise pas l'exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l'exclusion ci-dessus ne s'appliquera pas à votre égard.
+EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d'autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
diff --git a/PSAppDeplyToolkit/bin/i386/handle/handle.exe b/PSAppDeplyToolkit/bin/i386/handle/handle.exe
new file mode 100644
index 0000000..39dc6ea
Binary files /dev/null and b/PSAppDeplyToolkit/bin/i386/handle/handle.exe differ
diff --git a/PSAppDeplyToolkit/lib/PSADT.UserInterface.dll b/PSAppDeplyToolkit/lib/PSADT.UserInterface.dll
new file mode 100644
index 0000000..c7f1cf5
Binary files /dev/null and b/PSAppDeplyToolkit/lib/PSADT.UserInterface.dll differ
diff --git a/PSAppDeplyToolkit/lib/PSADT.dll b/PSAppDeplyToolkit/lib/PSADT.dll
new file mode 100644
index 0000000..de3883a
Binary files /dev/null and b/PSAppDeplyToolkit/lib/PSADT.dll differ
diff --git a/PSAppDeplyToolkit/lib/System.ValueTuple.dll b/PSAppDeplyToolkit/lib/System.ValueTuple.dll
new file mode 100644
index 0000000..1cadbf3
Binary files /dev/null and b/PSAppDeplyToolkit/lib/System.ValueTuple.dll differ
diff --git a/PSAppDeplyToolkit/lib/Wpf.Ui.Abstractions.dll b/PSAppDeplyToolkit/lib/Wpf.Ui.Abstractions.dll
new file mode 100644
index 0000000..3877b2f
Binary files /dev/null and b/PSAppDeplyToolkit/lib/Wpf.Ui.Abstractions.dll differ
diff --git a/PSAppDeplyToolkit/lib/Wpf.Ui.Abstractions.xml b/PSAppDeplyToolkit/lib/Wpf.Ui.Abstractions.xml
new file mode 100644
index 0000000..e14e6d0
--- /dev/null
+++ b/PSAppDeplyToolkit/lib/Wpf.Ui.Abstractions.xml
@@ -0,0 +1,664 @@
+
+
+
+ Wpf.Ui.Abstractions
+
+
+
+
+ A component whose ViewModel is separate from the DataContext and can be navigated by INavigationView.
+
+ The type of the ViewModel associated with the view. This type optionally may implement to participate in navigation processes.
+
+
+
+ Gets the view model used by the view.
+ Optionally, it may implement and be navigated by INavigationView.
+
+
+
+
+ Notifies class about being navigated.
+
+
+
+
+ Asynchronously handles the event that is fired after the component is navigated to.
+
+ A task that represents the asynchronous operation.
+
+
+
+ Asynchronously handles the event that is fired before the component is navigated from.
+
+ A task that represents the asynchronous operation.
+
+
+
+ Provides a base class for navigation-aware components.
+
+
+
+
+
+
+
+ Handles the event that is fired after the component is navigated to.
+
+
+
+
+
+
+
+ Handles the event that is fired before the component is navigated from.
+
+
+
+
+ Defines a service that provides pages for navigation.
+
+
+
+
+ Retrieves a page of the specified type.
+
+ The type of the page to retrieve.
+ An instance of the specified page type, or null if the page is not found.
+
+
+
+ Represents errors that occur during navigation.
+
+
+
+
+ Initializes a new instance of the NavigationException class with a specified error message.
+
+ The message that describes the error.
+
+
+
+ Initializes a new instance of the NavigationException class with a specified error message and a reference to the inner exception that is the cause of this exception.
+
+ The exception that is the cause of the current exception.
+ The message that describes the error.
+
+
+
+ Provides extension methods for the INavigationViewPageProvider interface.
+
+
+
+
+ Retrieves a page of the specified type from the page service.
+
+ The type of the page to retrieve.
+ The page service instance.
+ An instance of the specified page type, or null if the page is not found.
+
+
+
+ Retrieves a page of the specified type from the page service.
+ Throws a NavigationException if the page is not found.
+
+ The type of the page to retrieve.
+ The page service instance.
+ An instance of the specified page type.
+ Thrown when the specified page type is not found.
+
+
+
+ Specifies that null is allowed as an input even if the corresponding type disallows it.
+
+
+
+
+ Indicates that the specified method parameter expects a constant.
+
+
+ This can be used to inform tooling that a constant should be used as an argument for the annotated parameter.
+
+
+
+
+ Indicates the minimum bound of the expected constant, inclusive.
+
+
+
+
+ Indicates the maximum bound of the expected constant, inclusive.
+
+
+
+
+ Specifies that null is disallowed as an input even if the corresponding type allows it.
+
+
+
+
+ Applied to a method that will never return under any circumstance.
+
+
+
+
+ Specifies that the method will not return if the associated Boolean parameter is passed the specified value.
+
+
+
+
+ Initializes the attribute with the specified parameter value.
+
+
+ The condition parameter value. Code after the method will be considered unreachable
+ by diagnostics if the argument to the associated parameter matches this value.
+
+
+
+
+ Gets the condition parameter value.
+
+
+
+
+ Indicates that an API is experimental and it may change in the future.
+
+
+ This attribute allows call sites to be flagged with a diagnostic that indicates that an experimental
+ feature is used. Authors can use this attribute to ship preview features in their assemblies.
+
+
+
+
+ Initializes a new instance of the class,
+ specifying the ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+ The ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+
+
+ Gets the ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+ The unique diagnostic ID.
+
+ The diagnostic ID is shown in build output for warnings and errors.
+ This property represents the unique ID that can be used to suppress the warnings or errors, if needed.
+
+
+
+
+ Gets or sets the URL for corresponding documentation.
+ The API accepts a format string instead of an actual URL, creating a generic URL that includes the diagnostic ID.
+
+ The format string that represents a URL to corresponding documentation.
+ An example format string is https://contoso.com/obsoletion-warnings/{0}.
+
+
+
+ Specifies that an output may be null even if the corresponding type disallows it.
+
+
+
+
+ Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it.
+
+
+
+
+ Initializes the attribute with the specified return value condition.
+
+ The return value condition. If the method returns this value, the associated parameter may be null.
+
+
+
+ Gets the return value condition.
+
+
+
+
+ Specifies that the method or property will ensure that the listed field and property members have not-null values.
+
+
+
+
+ Initializes the attribute with a field or property member.
+
+ The field or property member that is promised to be not-null.
+
+
+
+ Initializes the attribute with the list of field and property members.
+
+ The list of field and property members that are promised to be not-null.
+
+
+
+ Gets field or property member names.
+
+
+
+
+ Specifies that the method or property will ensure that the listed field and property
+ members have not-null values when returning with the specified return value condition.
+
+
+
+
+ Initializes the attribute with the specified return value condition and a field or property member.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+ The field or property member that is promised to be not-null.
+
+
+
+ Initializes the attribute with the specified return value condition and list of field and property members.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+ The list of field and property members that are promised to be not-null.
+
+
+
+ Gets the return value condition.
+
+
+
+
+ Gets field or property member names.
+
+
+
+
+ Specifies that an output will not be null even if the corresponding type allows it.
+ Specifies that an input argument was not null when the call returns.
+
+
+
+
+ Specifies that the output will be non-null if the named parameter is non-null.
+
+
+
+
+ Initializes the attribute with the associated parameter name.
+
+ The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
+
+
+
+ Gets the associated parameter name.
+
+
+
+
+ Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it.
+
+
+
+
+ Initializes the attribute with the specified return value condition.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+
+
+ Gets the return value condition.
+
+
+
+ Specifies that this constructor sets all required members for the current type,
+ and callers do not need to set any required members themselves.
+
+
+
+
+ Specifies the syntax used in a string.
+
+
+
+
+ Initializes the with the identifier of the syntax used.
+
+ The syntax identifier.
+
+
+ Initializes the with the identifier of the syntax used.
+ The syntax identifier.
+ Optional arguments associated with the specific syntax employed.
+
+
+ Gets the identifier of the syntax used.
+
+
+ Optional arguments associated with the specific syntax employed.
+
+
+ The syntax identifier for strings containing composite formats for string formatting.
+
+
+ The syntax identifier for strings containing date format specifiers.
+
+
+ The syntax identifier for strings containing date and time format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing JavaScript Object Notation (JSON).
+
+
+ The syntax identifier for strings containing numeric format specifiers.
+
+
+ The syntax identifier for strings containing regular expressions.
+
+
+ The syntax identifier for strings containing time format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing URIs.
+
+
+ The syntax identifier for strings containing XML.
+
+
+
+ Used to indicate a byref escapes and is not scoped.
+
+
+
+ There are several cases where the C# compiler treats a as implicitly
+ - where the compiler does not allow the to escape the method.
+
+
+ For example:
+
+ - for instance methods.
+ - parameters that refer to types.
+ - parameters.
+
+
+
+ This attribute is used in those instances where the should be allowed to escape.
+
+
+ Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for
+ API authors to understand the lifetime implications of applying this attribute and how it may impact their users.
+
+
+
+
+ Represent a type can be used to index a collection either from the start or the end.
+
+ Index is used by the C# compiler to support the new index syntax
+
+ int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
+ int lastElement = someArray[^1]; // lastElement = 5
+
+
+
+
+ Construct an Index using a value and indicating if the index is from the start or from the end.
+ The index value. it has to be zero or positive number.
+ Indicating if the index is from the start or from the end.
+
+ If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
+
+
+
+ Create an Index pointing at first element.
+
+
+ Create an Index pointing at beyond last element.
+
+
+ Create an Index from the start at the position indicated by the value.
+ The index value from the start.
+
+
+ Create an Index from the end at the position indicated by the value.
+ The index value from the end.
+
+
+ Returns the index value.
+
+
+ Indicates whether the index is from the start or the end.
+
+
+ Calculate the offset from the start using the giving collection length.
+ The length of the collection that the Index will be used with. length has to be a positive value
+
+ For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
+ we don't validate either the returned offset is greater than the input length.
+ It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
+ then used to index a collection will get out of range exception which will be same affect as the validation.
+
+
+
+ Indicates whether the current Index object is equal to another object of the same type.
+ An object to compare with this object
+
+
+ Indicates whether the current Index object is equal to another Index object.
+ An object to compare with this object
+
+
+ Returns the hash code for this instance.
+
+
+ Converts integer number to an Index.
+
+
+ Converts the value of the current Index object to its equivalent string representation.
+
+
+
+ Indicates the type of the async method builder that should be used by a language compiler to
+ build the attributed async method or to build the attributed type when used as the return type
+ of an async method.
+
+
+
+ Initializes the .
+ The of the associated builder.
+
+
+ Gets the of the associated builder.
+
+
+
+ An attribute that allows parameters to receive the expression of other parameters.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The condition parameter value.
+
+
+
+ Gets the parameter name the expression is retrieved from.
+
+
+
+
+ Initialize the attribute to refer to the method on the type.
+
+ The type of the builder to use to construct the collection.
+ The name of the method on the builder to use to construct the collection.
+
+ must refer to a static method that accepts a single parameter of
+ type and returns an instance of the collection being built containing
+ a copy of the data from that span. In future releases of .NET, additional patterns may be supported.
+
+
+
+
+ Gets the type of the builder to use to construct the collection.
+
+
+
+
+ Gets the name of the method on the builder to use to construct the collection.
+
+
+ This should match the metadata name of the target method.
+ For example, this might be ".ctor" if targeting the type's constructor.
+
+
+
+
+ Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
+
+
+
+
+ Creates a new instance of the type.
+
+ The name of the feature to indicate.
+
+
+
+ The name of the compiler feature.
+
+
+
+
+ If true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand .
+
+
+
+
+ The used for the ref structs C# feature.
+
+
+
+
+ The used for the required members C# feature.
+
+
+
+
+ Indicates which arguments to a method involving an interpolated string handler should be passed to that handler.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The name of the argument that should be passed to the handler.
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Initializes a new instance of the class.
+
+ The names of the arguments that should be passed to the handler.
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Gets the names of the arguments that should be passed to the handler.
+
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Indicates the attributed type is to be used as an interpolated string handler.
+
+
+
+
+ Reserved to be used by the compiler for tracking metadata.
+ This class should not be used by developers in source code.
+
+
+
+
+ Used to indicate to the compiler that a method should be called
+ in its containing module's initializer.
+
+
+ When one or more valid methods
+ with this attribute are found in a compilation, the compiler will
+ emit a module initializer which calls each of the attributed methods.
+
+ Certain requirements are imposed on any method targeted with this attribute:
+ - The method must be `static`.
+ - The method must be an ordinary member method, as opposed to a property accessor, constructor, local function, etc.
+ - The method must be parameterless.
+ - The method must return `void`.
+ - The method must not be generic or be contained in a generic type.
+ - The method's effective accessibility must be `internal` or `public`.
+
+ The specification for module initializers in the .NET runtime can be found here:
+ https://github.com/dotnet/runtime/blob/main/docs/design/specs/Ecma-335-Augments.md#module-initializer
+
+
+
+
+ Specifies the priority of a member in overload resolution. When unspecified, the default priority is 0.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The priority of the attributed member. Higher numbers are prioritized, lower numbers are deprioritized. 0 is the default if no attribute is present.
+
+
+
+ The priority of the member.
+
+
+
+
+ Indicates that a method will allow a variable number of arguments in its invocation.
+
+
+
+
+ Specifies that a type has required members or that a member is required.
+
+
+
+
+ Reserved for use by a compiler for tracking metadata.
+ This attribute should not be used by developers in source code.
+
+
+
+
+ Used to indicate to the compiler that the .locals init flag should not be set in method headers.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class with the specified message.
+
+ An optional message associated with this attribute instance.
+
+
+
+ Returns the optional message associated with this attribute instance.
+
+
+
+
+ Returns the optional URL associated with this attribute instance.
+
+
+
+
diff --git a/PSAppDeplyToolkit/lib/Wpf.Ui.Tray.dll b/PSAppDeplyToolkit/lib/Wpf.Ui.Tray.dll
new file mode 100644
index 0000000..aa03b9f
Binary files /dev/null and b/PSAppDeplyToolkit/lib/Wpf.Ui.Tray.dll differ
diff --git a/PSAppDeplyToolkit/lib/Wpf.Ui.Tray.xml b/PSAppDeplyToolkit/lib/Wpf.Ui.Tray.xml
new file mode 100644
index 0000000..48b576a
--- /dev/null
+++ b/PSAppDeplyToolkit/lib/Wpf.Ui.Tray.xml
@@ -0,0 +1,2043 @@
+
+
+
+ Wpf.Ui.Tray
+
+
+
+
+ Represents the implementation of icon in the tray menu as .
+
+
+
+ <tray:NotifyIcon
+ Grid.Row="0"
+ FocusOnLeftClick="True"
+ Icon="pack://application:,,,/Assets/wpfui.png"
+ MenuOnRightClick="True"
+ TooltipText="WPF UI">
+ <tray:NotifyIcon.Menu>
+ <ContextMenu ItemsSource = "{Binding ViewModel.TrayMenuItems, Mode=OneWay}" />
+ </tray:NotifyIcon.Menu>
+ </tray:NotifyIcon>
+
+
+
+
+
+ Gets or sets a value indicating whether the control is disposed.
+
+
+
+
+ Gets a value indicating whether the icon is registered in the tray menu.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether to show the on single right click.
+
+
+
+
+ Gets or sets a value indicating whether to focus the on single left click.
+
+
+
+
+ Gets or sets the context menu.
+
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+
+ Triggered when the user left-clicks on the .
+
+
+
+
+ Triggered when the user double-clicks the with the left mouse button.
+
+
+
+
+ Triggered when the user right-clicks on the .
+
+
+
+
+ Triggered when the user double-clicks the with the right mouse button.
+
+
+
+
+ Triggered when the user middle-clicks on the .
+
+
+
+
+ Triggered when the user double-clicks the with the middle mouse button.
+
+
+
+
+ Finalizes an instance of the class.
+
+
+
+
+ Tries to register the in the shell.
+
+
+
+
+ Tries to unregister the from the shell.
+
+
+
+
+
+
+
+ This virtual method is called when is left-clicked and it raises the .
+
+
+
+
+ This virtual method is called when is left-clicked and it raises the .
+
+
+
+
+ This virtual method is called when is left-clicked and it raises the .
+
+
+
+
+ This virtual method is called when is left-clicked and it raises the .
+
+
+
+
+ This virtual method is called when is left-clicked and it raises the .
+
+
+
+
+ This virtual method is called when is left-clicked and it raises the .
+
+
+
+
+ If disposing equals , the method has been called directly or indirectly
+ by a user's code. Managed and unmanaged resources can be disposed. If disposing equals ,
+ the method has been called by the runtime from inside the finalizer and you should not
+ reference other objects.
+ Only unmanaged resources can be disposed.
+
+ If disposing equals , dispose all managed and unmanaged resources.
+
+
+
+ This virtual method is called when of is changed.
+
+ New context menu object.
+
+
+
+ Facilitates the creation of a hIcon.
+
+
+
+
+ Tries to take the icon pointer assigned to the application.
+
+
+
+
+ Tries to allocate an icon to memory and fetch a pointer to it.
+
+ Image source.
+
+
+
+ Represents an icon in the tray menu.
+
+
+
+
+ Gets or sets the notify icon shell data.
+
+
+
+
+ Gets or sets a value indicating whether the icon is currently registered in the tray area.
+
+
+
+
+ Gets or sets the Shell identifier of the icon.
+
+
+
+
+ Gets or sets the ToolTip text displayed when the mouse pointer rests on a notification area icon.
+
+
+
+
+ Gets or sets the of the tray icon.
+
+
+
+
+ Gets or sets the hWnd that will receive messages for the icon.
+
+
+
+
+ Gets or sets the menu displayed when the icon is right-clicked.
+
+
+
+
+ Gets or sets a value indicating whether to focus the on single left click.
+
+
+
+
+ Gets or sets a value indicating whether to show the on single right click.
+
+
+
+
+ A callback function that processes messages sent to a window.
+ The WNDPROC type defines a pointer to this callback function.
+
+
+
+
+ Tries to register the in the shell.
+
+
+
+
+ Tries to register the in the shell.
+
+
+
+
+ Tries to modify the icon of the in the shell.
+
+
+
+
+ Tries to modify the tooltip of the in the shell.
+
+
+
+
+ Tries to remove the from the shell.
+
+
+
+
+ Represents a contract with a service that provides methods for displaying the icon and menu in the tray area.
+
+
+
+
+ Gets the notify icon id.
+
+
+
+
+ Gets a value indicating whether the notify icon is registered in the tray.
+
+
+
+
+ Gets or sets the ToolTip text displayed when the mouse pointer rests on a notification area icon.
+
+
+
+
+ Gets or sets the context menu displayed after clicking the icon.
+
+
+
+
+ Gets or sets the of the tray icon.
+
+
+
+
+ Tries to register the Notify Icon in the shell.
+
+
+
+
+ Tries to unregister the Notify Icon from the shell.
+
+
+
+
+ Sets parent window of the tray icon.
+
+
+
+
+ Internal service for Notify Icon management.
+
+
+
+
+ Whether the control is disposed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Gets or sets a set of information for Shell32 to manipulate the icon.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Occurs when the application theme is changing.
+
+
+
+
+ Focus the application main window.
+
+
+
+
+ Shows the menu if it has been added.
+
+
+
+
+ This virtual method is called when tray icon is left-clicked and it raises the left click .
+
+
+
+
+ This virtual method is called when tray icon is left-clicked and it raises the left double click .
+
+
+
+
+ This virtual method is called when tray icon is left-clicked and it raises the right click .
+
+
+
+
+ This virtual method is called when tray icon is left-clicked and it raises the right double click .
+
+
+
+
+ This virtual method is called when tray icon is left-clicked and it raises the middle click .
+
+
+
+
+ This virtual method is called when tray icon is left-clicked and it raises the middle double click .
+
+
+
+
+ If disposing equals , the method has been called directly or indirectly
+ by a user's code. Managed and unmanaged resources can be disposed. If disposing equals ,
+ the method has been called by the runtime from inside the finalizer and you should not
+ reference other objects.
+ Only unmanaged resources can be disposed.
+
+ If disposing equals , dispose all managed and unmanaged resources.
+
+
+
+
+
+
+ Windows kernel module.
+
+
+
+
+ The Windows UI provides users with access to a wide variety of objects necessary to run applications and manage the operating system.
+
+
+
+
+ DATAOBJ_GET_ITEM_FLAGS. DOGIF_*.
+
+
+
+
+ Shell_NotifyIcon messages. NIM_*
+
+
+
+
+ Shell_NotifyIcon flags. NIF_*
+
+
+
+
+ Vista only.
+
+
+
+
+ Vista only.
+
+
+
+
+ The size of this structure, in bytes.
+
+
+
+
+ A handle to the window that receives notifications associated with an icon in the notification area.
+
+
+
+
+ The application-defined identifier of the taskbar icon. The Shell uses either (hWnd plus uID) or guidItem to identify which icon to operate on when Shell_NotifyIcon is invoked.
+ You can have multiple icons associated with a single hWnd by assigning each a different uID. If guidItem is specified, uID is ignored.
+
+
+
+
+ Flags that either indicate which of the other members of the structure contain valid data or provide additional information to the tooltip as to how it should display.
+
+
+
+
+ 0x00000001. The uCallbackMessage member is valid.
+
+
+
+
+ 0x00000002. The hIcon member is valid.
+
+
+
+
+ 0x00000004. The szTip member is valid.
+
+
+
+
+ The state of the icon. There are two flags that can be set independently.
+ NIS_HIDDEN = 1. The icon is hidden.
+ NIS_SHAREDICON = 2. The icon is shared.
+
+
+
+
+ Prior to Vista this was a union of uTimeout and uVersion. As of Vista, uTimeout has been deprecated.
+
+
+
+
+ Sets the User Model AppID for the current process, enabling Windows to retrieve this ID
+
+ The string ID to be assigned
+
+
+
+ Retrieves the User Model AppID that has been explicitly set for the current process via SetCurrentProcessExplicitAppUserModelID
+
+ Out parameter that receives the string ID.
+ An HRESULT indicating success (S_OK) or failure of the operation. If the function fails, the returned AppID is null.
+
+
+
+ USER procedure declarations, constant definitions and macros.
+
+
+
+
+ SetWindowPos options
+
+
+
+
+ EnableMenuItem uEnable values, MF_*
+
+
+
+
+ Possible return value for EnableMenuItem
+
+
+
+
+ Menu item element.
+
+
+
+
+ SCF_ISSECURE
+
+
+
+
+ WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
+
+
+
+
+ Hit test returned error.
+
+
+
+
+ Hit test returned transparent.
+
+
+
+
+ On the screen background or on a dividing line between windows.
+
+
+
+
+ In a client area.
+
+
+
+
+ In a title bar.
+
+
+
+
+ In a window menu or in a Close button in a child window.
+
+
+
+
+ In a size box (same as HTSIZE).
+
+
+
+
+ In a menu.
+
+
+
+
+ In a horizontal scroll bar.
+
+
+
+
+ In the vertical scroll bar.
+
+
+
+
+ In a Minimize button.
+
+
+
+
+ In a Maximize button.
+
+
+
+
+ In the left border of a resizable window (the user can click the mouse to resize the window horizontally).
+
+
+
+
+ In the right border of a resizable window (the user can click the mouse to resize the window horizontally).
+
+
+
+
+ In the upper-horizontal border of a window.
+
+
+
+
+ Window long flags.
+
+
+
+
+
+ Sets a new extended window style.
+
+
+
+
+ Sets a new application instance handle.
+
+
+
+
+ Sets a new hWnd parent.
+
+
+
+
+ Sets a new identifier of the child window. The window cannot be a top-level window.
+
+
+
+
+ Sets a new window style.
+
+
+
+
+ Sets the user data associated with the window.
+ This data is intended for use by the application that created the window. Its value is initially zero.
+
+
+
+
+ Sets a new address for the window procedure.
+ You cannot change this attribute if the window does not belong to the same process as the calling thread.
+
+
+
+
+ Sets new extra information that is private to the application, such as handles or pointers.
+
+
+
+
+ Sets the return value of a message processed in the dialog box procedure.
+
+
+
+
+ Sets the new address of the dialog box procedure.
+
+
+
+
+ Window composition attributes.
+
+
+
+
+ DWM window accent state.
+
+
+
+
+ WCA window accent policy.
+
+
+
+
+ CS_*
+
+
+
+
+ MSGFLT_*. New in Vista. Realiased in Windows 7.
+
+
+
+
+ Resets the window message filter for hWnd to the default. Any message allowed globally or process-wide will get through, but any message not included in those two categories, and which comes from a lower privileged process, will be blocked.
+
+
+
+
+ Allows the message through the filter. This enables the message to be received by hWnd, regardless of the source of the message, even it comes from a lower privileged process.
+
+
+
+
+ Blocks the message to be delivered to hWnd if it comes from a lower privileged process, unless the message is allowed process-wide by using the ChangeWindowMessageFilter function or globally.
+
+
+
+
+ MSGFLTINFO.
+
+
+
+
+ Win7 only.
+
+
+
+
+ Window message values, WM_*
+
+
+
+
+ This is the hard-coded message value used by WinForms for Shell_NotifyIcon.
+ It's relatively safe to reuse.
+
+
+
+
+ WindowStyle values, WS_*
+
+
+
+
+ Window style extended values, WS_EX_*
+
+
+
+
+ SystemMetrics. SM_*
+
+
+
+
+ ShowWindow options
+
+
+
+
+ Contains window class information. It is used with the and GetClassInfoEx functions.
+
+
+
+
+ The size, in bytes, of this structure. Set this member to sizeof(WNDCLASSEX). Be sure to set this member before calling the GetClassInfoEx function.
+
+
+
+
+ The class style(s). This member can be any combination of the Class Styles.
+
+
+
+
+ A pointer to the window procedure. You must use the CallWindowProc function to call the window procedure. For more information, see WindowProc.
+
+
+
+
+ The number of extra bytes to allocate following the window-class structure. The system initializes the bytes to zero.
+
+
+
+
+ The number of extra bytes to allocate following the window instance. The system initializes the bytes to zero. If an application uses WNDCLASSEX to register a dialog box created by using the CLASS directive in the resource file, it must set this member to DLGWINDOWEXTRA.
+
+
+
+
+ A handle to the instance that contains the window procedure for the class.
+
+
+
+
+ A handle to the class icon. This member must be a handle to an icon resource. If this member is NULL, the system provides a default icon.
+
+
+
+
+ A handle to the class cursor. This member must be a handle to a cursor resource. If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window.
+
+
+
+
+ A handle to the class background brush. This member can be a handle to the brush to be used for painting the background, or it can be a color value.
+
+
+
+
+ Pointer to a null-terminated character string that specifies the resource name of the class menu, as the name appears in the resource file. If you use an integer to identify the menu, use the MAKEINTRESOURCE macro. If this member is NULL, windows belonging to this class have no default menu.
+
+
+
+
+ A pointer to a null-terminated string or is an atom. If this parameter is an atom, it must be a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpszClassName; the high-order word must be zero.
+
+
+
+
+ A handle to a small icon that is associated with the window class. If this member is NULL, the system searches the icon resource specified by the hIcon member for an icon of the appropriate size to use as the small icon.
+
+
+
+
+ Delegate declaration that matches native WndProc signatures.
+
+
+
+
+ Delegate declaration that matches native WndProc signatures.
+
+
+
+
+ Delegate declaration that matches managed WndProc signatures.
+
+
+
+
+ The ReleaseDC function releases a device context (DC), freeing it for use by other applications.
+ The effect of the ReleaseDC function depends on the type of DC. It frees only common and window DCs. It has no effect on class or private DCs.
+
+ A handle to the window whose DC is to be released.
+ A handle to the DC to be released.
+ The return value indicates whether the DC was released. If the DC was released, the return value is 1. If the DC was not released, the return value is zero.
+
+
+
+ Calculates the required size of the window rectangle, based on the desired size of the client rectangle.
+ The window rectangle can then be passed to the CreateWindowEx function to create a window whose client area is the desired size.
+
+ A pointer to a RECT structure that contains the coordinates of the top-left and bottom-right corners of the desired client area.
+ The window style of the window whose required size is to be calculated. Note that you cannot specify the WS_OVERLAPPED style.
+ Indicates whether the window has a menu.
+ The extended window style of the window whose required size is to be calculated.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ [Using the ChangeWindowMessageFilter function is not recommended, as it has process-wide scope. Instead, use the ChangeWindowMessageFilterEx function to control access to specific windows as needed. ChangeWindowMessageFilter may not be supported in future versions of Windows.
+ Adds or removes a message from the User Interface Privilege Isolation(UIPI) message filter.
+
+ The message to add to or remove from the filter.
+ The action to be performed. One of the following values.
+ if successful; otherwise, . To get extended error information, call Kernel32.GetLastError().
+
+
+
+ Modifies the User Interface Privilege Isolation (UIPI) message filter for a specified window.
+
+ A handle to the window whose UIPI message filter is to be modified.
+ The message that the message filter allows through or blocks.
+ The action to be performed.
+ Optional pointer to a structure.
+ If the function succeeds, it returns ; otherwise, it returns . To get extended error information, call Kernel32.GetLastError().
+
+
+
+ Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
+ Unicode declaration for
+
+ A handle to the window whose window procedure is to receive the message.
+ The message to be posted.
+ Additional message-specific information.
+ Additional message-specific information.~
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
+ ANSI declaration for
+
+ A handle to the window whose window procedure is to receive the message.
+ The message to be posted.
+ Additional message-specific information.
+ Additional message-specific information.~
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
+
+ A handle to the window whose window procedure is to receive the message.
+ The message to be posted.
+ Additional message-specific information.
+ Additional message-specific information.~
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
+
+ A handle to the window whose window procedure will receive the message.
+ The message to be sent.
+ Additional message-specific information.
+ Additional message-specific information.~
+ The return value specifies the result of the message processing; it depends on the message sent.
+
+
+
+ Creates an overlapped, pop-up, or child window with an extended window style; otherwise,
+ this function is identical to the CreateWindow function. For more information about
+ creating a window and for full descriptions of the other parameters of CreateWindowEx, see CreateWindow.
+
+ The extended window style of the window being created.
+ A null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function.
+ The window name. If the window style specifies a title bar, the window title pointed to by lpWindowName is displayed in the title bar.
+ The style of the window being created. This parameter can be a combination of the window style values, plus the control styles indicated in the Remarks section.
+ The initial horizontal position of the window. For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, in screen coordinates.
+ The initial vertical position of the window. For an overlapped or pop-up window, the y parameter is the initial y-coordinate of the window's upper-left corner, in screen coordinates.
+ The width, in device units, of the window. For overlapped windows, nWidth is the window's width, in screen coordinates, or CW_USEDEFAULT.
+ The height, in device units, of the window. For overlapped windows, nHeight is the window's height, in screen coordinates. If the nWidth parameter is set to CW_USEDEFAULT, the system ignores nHeight.
+ A handle to the parent or owner window of the window being created. To create a child window or an owned window, supply a valid window handle. This parameter is optional for pop-up windows.
+ A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used.
+ A handle to the instance of the module to be associated with the window.
+ Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns.
+ If the function succeeds, the return value is a handle to the new window.
+
+
+
+ Creates an overlapped, pop-up, or child window with an extended window style; otherwise,
+ this function is identical to the CreateWindow function. For more information about
+ creating a window and for full descriptions of the other parameters of CreateWindowEx, see CreateWindow.
+
+ The extended window style of the window being created.
+ A null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function.
+ The window name. If the window style specifies a title bar, the window title pointed to by lpWindowName is displayed in the title bar.
+ The style of the window being created. This parameter can be a combination of the window style values, plus the control styles indicated in the Remarks section.
+ The initial horizontal position of the window. For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, in screen coordinates.
+ The initial vertical position of the window. For an overlapped or pop-up window, the y parameter is the initial y-coordinate of the window's upper-left corner, in screen coordinates.
+ The width, in device units, of the window. For overlapped windows, nWidth is the window's width, in screen coordinates, or CW_USEDEFAULT.
+ The height, in device units, of the window. For overlapped windows, nHeight is the window's height, in screen coordinates. If the nWidth parameter is set to CW_USEDEFAULT, the system ignores nHeight.
+ A handle to the parent or owner window of the window being created. To create a child window or an owned window, supply a valid window handle. This parameter is optional for pop-up windows.
+ A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used.
+ A handle to the instance of the module to be associated with the window.
+ Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns.
+ If the function succeeds, the return value is a handle to the new window.
+
+
+
+ Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
+ Unicode declaration for
+
+ A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function.
+ If the function succeeds, the return value is a class atom that uniquely identifies the class being registered.
+
+
+
+ Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
+ ANSI declaration for
+
+ A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function.
+ If the function succeeds, the return value is a class atom that uniquely identifies the class being registered.
+
+
+
+ Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
+
+ A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function.
+ If the function succeeds, the return value is a class atom that uniquely identifies the class being registered.
+
+
+
+ Calls the default window procedure to provide default processing for any window messages that an application does not process.
+ This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure.
+ Unicode declaration for
+
+ A handle to the window procedure that received the message.
+ The message.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.~
+ The return value is the result of the message processing and depends on the message.
+
+
+
+ Calls the default window procedure to provide default processing for any window messages that an application does not process.
+ This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure.
+ ANSI declaration for
+
+ A handle to the window procedure that received the message.
+ The message.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.~
+ The return value is the result of the message processing and depends on the message.
+
+
+
+ Calls the default window procedure to provide default processing for any window messages that an application does not process.
+ This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure.
+
+ A handle to the window procedure that received the message.
+ The message.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.~
+ The return value is the result of the message processing and depends on the message.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ New window style.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified offset.
+
+
+
+ Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified offset.
+
+
+
+ Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified offset.
+
+
+
+ Destroys an icon and frees any memory the icon occupied.
+
+ A handle to the icon to be destroyed. The icon must not be in use.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Determines whether the specified window handle identifies an existing window.
+
+ A handle to the window to be tested.
+ If the window handle identifies an existing window, the return value is nonzero.
+
+
+
+ Destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus from it.
+
+ A handle to the window to be destroyed.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.
+
+ A handle to the window.
+ A pointer to a RECT structure that receives the screen coordinates of the upper-left and lower-right corners of the window.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Determines the visibility state of the specified window.
+
+ A handle to the window to be tested.
+ If the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero.
+
+
+
+ Determines whether the specified window is enabled for mouse and keyboard input.
+
+ A handle to the window to be tested.
+ If the window is enabled, the return value is nonzero.
+
+
+
+ The MonitorFromWindow function retrieves a handle to the display monitor that has the largest area of intersection with the bounding rectangle of a specified window.
+
+ A handle to the window of interest.
+ Determines the function's return value if the window does not intersect any display monitor.
+ If the window intersects one or more display monitor rectangles, the return value is an HMONITOR handle to the display monitor that has the largest area of intersection with the window.
+
+
+
+ Retrieves the specified system metric or system configuration setting.
+ Note that all dimensions retrieved by GetSystemMetrics are in pixels.
+
+ The system metric or configuration setting to be retrieved. This parameter can be one of the values.
+ Note that all SM_CX* values are widths and all SM_CY* values are heights. Also note that all settings designed to return Boolean data represent as any nonzero value, and as a zero value.
+ If the function succeeds, the return value is the requested system metric or configuration setting.
+
+
+
+ Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages.
+ Unicode declaration for
+
+ The message to be registered.
+ If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.
+
+
+
+ Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages.
+ ANSI declaration for
+
+ The message to be registered.
+ If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.
+
+
+
+ Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages.
+
+ The message to be registered.
+ If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.
+
+
+
+ Activates a window. The window must be attached to the calling thread's message queue.
+
+ A handle to the top-level window to be activated.
+ If the function succeeds, the return value is the handle to the window that was previously active.
+
+
+
+ Brings the thread that created the specified window into the foreground and activates the window.
+ Keyboard input is directed to the window, and various visual cues are changed for the user.
+ The system assigns a slightly higher priority to the thread that created the foreground window than it does to other threads.
+
+ A handle to the window that should be activated and brought to the foreground.
+ If the window was brought to the foreground, the return value is nonzero.
+
+
+
+ Enables, disables, or grays the specified menu item.
+
+ A handle to the menu.
+ The menu item to be enabled, disabled, or grayed, as determined by the uEnable parameter.
+ Controls the interpretation of the uIDEnableItem parameter and indicate whether the menu item is enabled, disabled, or grayed.
+ The return value specifies the previous state of the menu item (it is either MF_DISABLED, MF_ENABLED, or MF_GRAYED). If the menu item does not exist, the return value is -1 ().
+
+
+
+ The SetWindowRgn function sets the window region of a window. The window region determines the area within the window where the system permits drawing. The system does not display any portion of a window that lies outside of the window region.
+
+ A handle to the window whose window region is to be set.
+ A handle to a region. The function sets the window region of the window to this region.
+ Specifies whether the system redraws the window after setting the window region. If bRedraw is , the system does so; otherwise, it does not.
+ Native method returned HRESULT.
+
+
+
+ Changes the size, position, and Z order of a child, pop-up, or top-level window. These windows are ordered according to their appearance on the screen. The topmost window receives the highest rank and is the first window in the Z order.
+
+ A handle to the window.
+ A handle to the window to precede the positioned window in the Z order.
+ The new position of the left side of the window, in client coordinates.
+ The new position of the top of the window, in client coordinates.
+ The new width of the window, in pixels.
+ The new height of the window, in pixels.
+ The window sizing and positioning flags.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Sets the process-default DPI awareness to system-DPI awareness. This is equivalent to calling SetProcessDpiAwarenessContext with a DPI_AWARENESS_CONTEXT value of DPI_AWARENESS_CONTEXT_SYSTEM_AWARE.
+
+
+
+
+ Sets various information regarding DWM window attributes.
+
+
+
+
+ Sets various information regarding DWM window attributes.
+
+
+
+
+ Returns the dots per inch (dpi) value for the specified window.
+
+ The window that you want to get information about.
+ The DPI for the window, which depends on the DPI_AWARENESS of the window.
+
+
+
+ Returns the dots per inch (dpi) value for the specified window.
+
+ The window that you want to get information about.
+ The DPI for the window, which depends on the DPI_AWARENESS of the window.
+
+
+
+ Base implementation of the notify icon service.
+
+
+
+
+
+
+
+ This virtual method is called when the user clicks the left mouse button on the tray icon.
+
+
+
+
+ This virtual method is called when the user double-clicks the left mouse button on the tray icon.
+
+
+
+
+ This virtual method is called when the user clicks the right mouse button on the tray icon.
+
+
+
+
+ This virtual method is called when the user double-clicks the right mouse button on the tray icon.
+
+
+
+
+ This virtual method is called when the user clicks the middle mouse button on the tray icon.
+
+
+
+
+ This virtual method is called when the user double-clicks the middle mouse button on the tray icon.
+
+
+
+
+ Event triggered on successful navigation.
+
+ Source of the event, which should be the current navigation instance.
+ Event data containing information about the navigation event.
+
+
+
+ Singleton containing persistent information about icons in the tray menu for application session.
+
+
+
+
+ Gets or sets the collection of registered tray icons.
+
+
+
+
+ Manages the Win32 API and Windows messages.
+
+
+
+
+ Gets or sets the id of the hooked element.
+
+
+
+
+ Initializes a new instance of the class, creating a new hWnd as a child with transparency parameters, no size, and in the default position. It attaches the default delegation to the messages it receives.
+
+ The name of the created window.
+ Parent of the created window.
+
+
+
+ Responsible for managing the icons in the Tray bar.
+
+
+
+
+ Tries to remove the from the shell.
+
+
+
+
+ Gets application source.
+
+
+
+
+ Specifies that null is allowed as an input even if the corresponding type disallows it.
+
+
+
+
+ Indicates that the specified method parameter expects a constant.
+
+
+ This can be used to inform tooling that a constant should be used as an argument for the annotated parameter.
+
+
+
+
+ Indicates the minimum bound of the expected constant, inclusive.
+
+
+
+
+ Indicates the maximum bound of the expected constant, inclusive.
+
+
+
+
+ Specifies that null is disallowed as an input even if the corresponding type allows it.
+
+
+
+
+ Applied to a method that will never return under any circumstance.
+
+
+
+
+ Specifies that the method will not return if the associated Boolean parameter is passed the specified value.
+
+
+
+
+ Initializes the attribute with the specified parameter value.
+
+
+ The condition parameter value. Code after the method will be considered unreachable
+ by diagnostics if the argument to the associated parameter matches this value.
+
+
+
+
+ Gets the condition parameter value.
+
+
+
+
+ Indicates that an API is experimental and it may change in the future.
+
+
+ This attribute allows call sites to be flagged with a diagnostic that indicates that an experimental
+ feature is used. Authors can use this attribute to ship preview features in their assemblies.
+
+
+
+
+ Initializes a new instance of the class,
+ specifying the ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+ The ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+
+
+ Gets the ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+ The unique diagnostic ID.
+
+ The diagnostic ID is shown in build output for warnings and errors.
+ This property represents the unique ID that can be used to suppress the warnings or errors, if needed.
+
+
+
+
+ Gets or sets the URL for corresponding documentation.
+ The API accepts a format string instead of an actual URL, creating a generic URL that includes the diagnostic ID.
+
+ The format string that represents a URL to corresponding documentation.
+ An example format string is https://contoso.com/obsoletion-warnings/{0}.
+
+
+
+ Specifies that an output may be null even if the corresponding type disallows it.
+
+
+
+
+ Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it.
+
+
+
+
+ Initializes the attribute with the specified return value condition.
+
+ The return value condition. If the method returns this value, the associated parameter may be null.
+
+
+
+ Gets the return value condition.
+
+
+
+
+ Specifies that the method or property will ensure that the listed field and property members have not-null values.
+
+
+
+
+ Initializes the attribute with a field or property member.
+
+ The field or property member that is promised to be not-null.
+
+
+
+ Initializes the attribute with the list of field and property members.
+
+ The list of field and property members that are promised to be not-null.
+
+
+
+ Gets field or property member names.
+
+
+
+
+ Specifies that the method or property will ensure that the listed field and property
+ members have not-null values when returning with the specified return value condition.
+
+
+
+
+ Initializes the attribute with the specified return value condition and a field or property member.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+ The field or property member that is promised to be not-null.
+
+
+
+ Initializes the attribute with the specified return value condition and list of field and property members.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+ The list of field and property members that are promised to be not-null.
+
+
+
+ Gets the return value condition.
+
+
+
+
+ Gets field or property member names.
+
+
+
+
+ Specifies that an output will not be null even if the corresponding type allows it.
+ Specifies that an input argument was not null when the call returns.
+
+
+
+
+ Specifies that the output will be non-null if the named parameter is non-null.
+
+
+
+
+ Initializes the attribute with the associated parameter name.
+
+ The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
+
+
+
+ Gets the associated parameter name.
+
+
+
+
+ Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it.
+
+
+
+
+ Initializes the attribute with the specified return value condition.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+
+
+ Gets the return value condition.
+
+
+
+ Specifies that this constructor sets all required members for the current type,
+ and callers do not need to set any required members themselves.
+
+
+
+
+ Specifies the syntax used in a string.
+
+
+
+
+ Initializes the with the identifier of the syntax used.
+
+ The syntax identifier.
+
+
+ Initializes the with the identifier of the syntax used.
+ The syntax identifier.
+ Optional arguments associated with the specific syntax employed.
+
+
+ Gets the identifier of the syntax used.
+
+
+ Optional arguments associated with the specific syntax employed.
+
+
+ The syntax identifier for strings containing composite formats for string formatting.
+
+
+ The syntax identifier for strings containing date format specifiers.
+
+
+ The syntax identifier for strings containing date and time format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing JavaScript Object Notation (JSON).
+
+
+ The syntax identifier for strings containing numeric format specifiers.
+
+
+ The syntax identifier for strings containing regular expressions.
+
+
+ The syntax identifier for strings containing time format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing URIs.
+
+
+ The syntax identifier for strings containing XML.
+
+
+
+ Used to indicate a byref escapes and is not scoped.
+
+
+
+ There are several cases where the C# compiler treats a as implicitly
+ - where the compiler does not allow the to escape the method.
+
+
+ For example:
+
+ - for instance methods.
+ - parameters that refer to types.
+ - parameters.
+
+
+
+ This attribute is used in those instances where the should be allowed to escape.
+
+
+ Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for
+ API authors to understand the lifetime implications of applying this attribute and how it may impact their users.
+
+
+
+
+ Represent a type can be used to index a collection either from the start or the end.
+
+ Index is used by the C# compiler to support the new index syntax
+
+ int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
+ int lastElement = someArray[^1]; // lastElement = 5
+
+
+
+
+ Construct an Index using a value and indicating if the index is from the start or from the end.
+ The index value. it has to be zero or positive number.
+ Indicating if the index is from the start or from the end.
+
+ If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
+
+
+
+ Create an Index pointing at first element.
+
+
+ Create an Index pointing at beyond last element.
+
+
+ Create an Index from the start at the position indicated by the value.
+ The index value from the start.
+
+
+ Create an Index from the end at the position indicated by the value.
+ The index value from the end.
+
+
+ Returns the index value.
+
+
+ Indicates whether the index is from the start or the end.
+
+
+ Calculate the offset from the start using the giving collection length.
+ The length of the collection that the Index will be used with. length has to be a positive value
+
+ For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
+ we don't validate either the returned offset is greater than the input length.
+ It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
+ then used to index a collection will get out of range exception which will be same affect as the validation.
+
+
+
+ Indicates whether the current Index object is equal to another object of the same type.
+ An object to compare with this object
+
+
+ Indicates whether the current Index object is equal to another Index object.
+ An object to compare with this object
+
+
+ Returns the hash code for this instance.
+
+
+ Converts integer number to an Index.
+
+
+ Converts the value of the current Index object to its equivalent string representation.
+
+
+ Represent a range has start and end indexes.
+
+ Range is used by the C# compiler to support the range syntax.
+
+ int[] someArray = new int[5] { 1, 2, 3, 4, 5 };
+ int[] subArray1 = someArray[0..2]; // { 1, 2 }
+ int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
+
+
+
+
+ Represent the inclusive start index of the Range.
+
+
+ Represent the exclusive end index of the Range.
+
+
+ Construct a Range object using the start and end indexes.
+ Represent the inclusive start index of the range.
+ Represent the exclusive end index of the range.
+
+
+ Indicates whether the current Range object is equal to another object of the same type.
+ An object to compare with this object
+
+
+ Indicates whether the current Range object is equal to another Range object.
+ An object to compare with this object
+
+
+ Returns the hash code for this instance.
+
+
+ Converts the value of the current Range object to its equivalent string representation.
+
+
+ Create a Range object starting from start index to the end of the collection.
+
+
+ Create a Range object starting from first element in the collection to the end Index.
+
+
+ Create a Range object starting from first element to the end.
+
+
+ Calculate the start offset and length of range object using a collection length.
+ The length of the collection that the range will be used with. length has to be a positive value.
+
+ For performance reason, we don't validate the input length parameter against negative values.
+ It is expected Range will be used with collections which always have non negative length/count.
+ We validate the range is inside the length scope though.
+
+
+
+
+ Indicates the type of the async method builder that should be used by a language compiler to
+ build the attributed async method or to build the attributed type when used as the return type
+ of an async method.
+
+
+
+ Initializes the .
+ The of the associated builder.
+
+
+ Gets the of the associated builder.
+
+
+
+ An attribute that allows parameters to receive the expression of other parameters.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The condition parameter value.
+
+
+
+ Gets the parameter name the expression is retrieved from.
+
+
+
+
+ Initialize the attribute to refer to the method on the type.
+
+ The type of the builder to use to construct the collection.
+ The name of the method on the builder to use to construct the collection.
+
+ must refer to a static method that accepts a single parameter of
+ type and returns an instance of the collection being built containing
+ a copy of the data from that span. In future releases of .NET, additional patterns may be supported.
+
+
+
+
+ Gets the type of the builder to use to construct the collection.
+
+
+
+
+ Gets the name of the method on the builder to use to construct the collection.
+
+
+ This should match the metadata name of the target method.
+ For example, this might be ".ctor" if targeting the type's constructor.
+
+
+
+
+ Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
+
+
+
+
+ Creates a new instance of the type.
+
+ The name of the feature to indicate.
+
+
+
+ The name of the compiler feature.
+
+
+
+
+ If true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand .
+
+
+
+
+ The used for the ref structs C# feature.
+
+
+
+
+ The used for the required members C# feature.
+
+
+
+
+ Indicates which arguments to a method involving an interpolated string handler should be passed to that handler.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The name of the argument that should be passed to the handler.
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Initializes a new instance of the class.
+
+ The names of the arguments that should be passed to the handler.
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Gets the names of the arguments that should be passed to the handler.
+
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Indicates the attributed type is to be used as an interpolated string handler.
+
+
+
+
+ Reserved to be used by the compiler for tracking metadata.
+ This class should not be used by developers in source code.
+
+
+
+
+ Used to indicate to the compiler that a method should be called
+ in its containing module's initializer.
+
+
+ When one or more valid methods
+ with this attribute are found in a compilation, the compiler will
+ emit a module initializer which calls each of the attributed methods.
+
+ Certain requirements are imposed on any method targeted with this attribute:
+ - The method must be `static`.
+ - The method must be an ordinary member method, as opposed to a property accessor, constructor, local function, etc.
+ - The method must be parameterless.
+ - The method must return `void`.
+ - The method must not be generic or be contained in a generic type.
+ - The method's effective accessibility must be `internal` or `public`.
+
+ The specification for module initializers in the .NET runtime can be found here:
+ https://github.com/dotnet/runtime/blob/main/docs/design/specs/Ecma-335-Augments.md#module-initializer
+
+
+
+
+ Specifies the priority of a member in overload resolution. When unspecified, the default priority is 0.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The priority of the attributed member. Higher numbers are prioritized, lower numbers are deprioritized. 0 is the default if no attribute is present.
+
+
+
+ The priority of the member.
+
+
+
+
+ Indicates that a method will allow a variable number of arguments in its invocation.
+
+
+
+
+ Specifies that a type has required members or that a member is required.
+
+
+
+
+ Reserved for use by a compiler for tracking metadata.
+ This attribute should not be used by developers in source code.
+
+
+
+
+ Used to indicate to the compiler that the .locals init flag should not be set in method headers.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class with the specified message.
+
+ An optional message associated with this attribute instance.
+
+
+
+ Returns the optional message associated with this attribute instance.
+
+
+
+
+ Returns the optional URL associated with this attribute instance.
+
+
+
+
diff --git a/PSAppDeplyToolkit/lib/Wpf.Ui.dll b/PSAppDeplyToolkit/lib/Wpf.Ui.dll
new file mode 100644
index 0000000..6aa3b61
Binary files /dev/null and b/PSAppDeplyToolkit/lib/Wpf.Ui.dll differ
diff --git a/PSAppDeplyToolkit/lib/Wpf.Ui.xml b/PSAppDeplyToolkit/lib/Wpf.Ui.xml
new file mode 100644
index 0000000..922ff73
--- /dev/null
+++ b/PSAppDeplyToolkit/lib/Wpf.Ui.xml
@@ -0,0 +1,9935 @@
+
+
+
+ Wpf.Ui
+
+
+
+
+ Available types of transitions.
+
+
+
+
+ None.
+
+
+
+
+ Change opacity.
+
+
+
+
+ Change opacity and slide from bottom.
+
+
+
+
+ Slide from bottom.
+
+
+
+
+ Slide from the right side.
+
+
+
+
+ Slide from the left side.
+
+
+
+
+ Provides tools for animation.
+
+
+
+ TransitionAnimationProvider.ApplyTransition(MyFrameworkElement, Transition.FadeIn, 500);
+
+
+
+
+
+ Attempts to apply an animation effect while adding content to the frame.
+
+ Currently rendered element.
+ Selected transition type.
+ Transition duration.
+ Returns if the transition was applied. Otherwise .
+
+
+
+ Allows updating the accents used by controls in the application by swapping dynamic resources.
+
+
+
+ ApplicationAccentColorManager.Apply(
+ Color.FromArgb(0xFF, 0xEE, 0x00, 0xBB),
+ ApplicationTheme.Dark,
+ false
+ );
+
+
+ ApplicationAccentColorManager.Apply(
+ ApplicationAccentColorManager.GetColorizationColor(),
+ ApplicationTheme.Dark,
+ false
+ );
+
+
+
+
+
+ The maximum value of the background HSV brightness after which the text on the accent will be turned dark.
+
+
+
+
+ Gets the SystemAccentColor.
+
+
+
+
+ Gets the of the SystemAccentColor.
+
+
+
+
+ Gets the SystemAccentColorPrimary.
+
+
+
+
+ Gets the of the SystemAccentColorPrimary.
+
+
+
+
+ Gets the SystemAccentColorSecondary.
+
+
+
+
+ Gets the of the SystemAccentColorSecondary.
+
+
+
+
+ Gets the SystemAccentColorTertiary.
+
+
+
+
+ Gets the of the SystemAccentColorTertiary.
+
+
+
+
+ Changes the color accents of the application based on the color entered.
+
+ Primary accent color.
+ If , the colors will be different.
+ If the color is taken from the Glass Color System, its brightness will be increased with the help of the operations on HSV space.
+
+
+
+ Changes the color accents of the application based on the entered colors.
+
+ Primary color.
+ Alternative light or dark color.
+ Second alternative light or dark color (most used).
+ Third alternative light or dark color.
+
+
+
+ Applies system accent color to the application.
+
+
+
+
+ Gets current Desktop Window Manager colorization color.
+ It should be the color defined in the system Personalization.
+
+
+
+
+ Updates application resources.
+
+
+
+
+ Theme in which an application using WPF UI is displayed.
+
+
+
+
+ Unknown application theme.
+
+
+
+
+ Dark application theme.
+
+
+
+
+ Light application theme.
+
+
+
+
+ High contract application theme.
+
+
+
+
+ Allows to manage the application theme by swapping resource dictionaries containing dynamic resources with color information.
+
+
+
+ ApplicationThemeManager.Apply(
+ ApplicationTheme.Light
+ );
+
+
+ if (ApplicationThemeManager.GetAppTheme() == ApplicationTheme.Dark)
+ {
+ ApplicationThemeManager.Apply(
+ ApplicationTheme.Light
+ );
+ }
+
+
+ ApplicationThemeManager.Changed += (theme, accent) =>
+ {
+ Debug.WriteLine($"Application theme changed to {theme.ToString()}");
+ };
+
+
+
+
+
+ Event triggered when the application's theme is changed.
+
+
+
+
+ Gets a value that indicates whether the application is currently using the high contrast theme.
+
+ if application uses high contrast theme.
+
+
+
+ Gets a value that indicates whether the Windows is currently using the high contrast theme.
+
+ if system uses high contrast theme.
+
+
+
+ Changes the current application theme.
+
+ Theme to set.
+ Whether the custom background effect should be applied.
+ Whether the color accents should be changed.
+
+
+
+ Applies Resources in the .
+
+
+
+
+ Gets currently set application theme.
+
+ if something goes wrong.
+
+
+
+ Gets currently set system theme.
+
+ if something goes wrong.
+
+
+
+ Gets a value that indicates whether the application is matching the system theme.
+
+ if the application has the same theme as the system.
+
+
+
+ Checks if the application and the operating system are currently working in a dark theme.
+
+
+
+
+ Checks if the application and the operating system are currently working in a light theme.
+
+
+
+
+ Tries to guess the currently set application theme.
+
+
+
+
+ Represents a window that is being observed for changes in appearance.
+
+
+
+
+ Initializes a new instance of the ObservedWindow class.
+
+ The handle of the window.
+ The backdrop type of the window.
+ Indicates whether to update accents.
+
+
+
+ Gets the root visual of the window.
+
+
+
+
+ Gets the handle of the window.
+
+
+
+
+ Gets the backdrop type of the window.
+
+
+
+
+ Gets a value indicating whether to update accents.
+
+
+
+
+ Gets a value indicating whether the window has a hook.
+
+
+
+
+ Adds a hook to the window.
+
+ The hook to add.
+
+
+
+ Removes a hook from the window.
+
+ The hook to remove.
+
+
+
+ Allows managing application dictionaries.
+
+
+
+
+ Gets the namespace, e.g. the library the resource is being searched for.
+
+
+
+
+ Shows whether the application contains the .
+
+ Any part of the resource name.
+ if it doesn't exist.
+
+
+
+ Gets the if exists.
+
+ Any part of the resource name.
+ , if it doesn't exist.
+
+
+
+ Shows whether the application contains the .
+
+ Any part of the resource name.
+ A valid for the replaced resource.
+ if the dictionary was updated. otherwise.
+
+
+
+ Windows 11 themes.
+
+
+
+
+ Unknown Windows theme.
+
+
+
+
+ Custom Windows theme.
+
+
+
+
+ Default light theme.
+
+
+
+
+ Default dark theme.
+
+
+
+
+ High-contrast theme: Desert
+
+
+
+
+ High-contrast theme: Acquatic
+
+
+
+
+ High-contrast theme: Dusk
+
+
+
+
+ High-contrast theme: Nightsky
+
+
+
+
+ Dark theme: Glow
+
+
+
+
+ Dark theme: Captured Motion
+
+
+
+
+ Light theme: Sunrise
+
+
+
+
+ Light theme: Flow
+
+
+
+
+ Provides information about Windows system themes.
+
+
+
+ var currentWindowTheme = SystemThemeManager.GetCachedSystemTheme();
+
+
+ SystemThemeManager.UpdateSystemThemeCache();
+ var currentWindowTheme = SystemThemeManager.GetCachedSystemTheme();
+
+
+
+
+
+ Gets the Windows glass color.
+
+
+
+
+ Gets a value indicating whether the system is currently using the high contrast theme.
+
+
+
+
+ Returns the Windows theme retrieved from the registry. If it has not been cached before, invokes the and then returns the currently obtained theme.
+
+ Currently cached Windows theme.
+
+
+
+ Refreshes the currently saved system theme.
+
+
+
+
+ Automatically updates the application background if the system theme or color is changed.
+ settings work globally and cannot be changed for each .
+
+
+
+ SystemThemeWatcher.Watch(this as System.Windows.Window);
+ SystemThemeWatcher.UnWatch(this as System.Windows.Window);
+
+
+ SystemThemeWatcher.Watch(
+ _serviceProvider.GetRequiredService<MainWindow>()
+ );
+
+
+
+
+
+ Watches the and applies the background effect and theme according to the system theme.
+
+ The window that will be updated.
+ Background effect to be applied when changing the theme.
+ If , the accents will be updated when the change is detected.
+
+
+
+ Unwatches the window and removes the hook to receive messages from the system.
+
+
+
+
+ Listens to system messages on the application windows.
+
+
+
+
+ Event triggered when application theme is updated.
+
+ Current application .
+ Current base system accent .
+
+
+
+ Facilitates the management of the window background.
+
+
+
+ WindowBackgroundManager.UpdateBackground(
+ observedWindow.RootVisual,
+ currentApplicationTheme,
+ observedWindow.Backdrop
+ );
+
+
+
+
+
+ Tries to apply dark theme to .
+
+
+
+
+ Tries to remove dark theme from .
+
+
+
+
+ Forces change to application background. Required if custom background effect was previously applied.
+
+
+
+
+ Provides UI Automation peer for the CardControl.
+
+
+
+
+ Provides UI Automation peer for the CardControl.
+
+
+
+
+ Represents a contract with the service that creates .
+
+
+
+ <ContentPresenter x:Name="RootContentDialogPresenter" Grid.Row="0" />
+
+
+ IContentDialogService contentDialogService = new ContentDialogService();
+ contentDialogService.SetContentPresenter(RootContentDialogPresenter);
+
+ await _contentDialogService.ShowAsync(
+ new ContentDialog(){
+ Title = "The cake?",
+ Content = "IS A LIE!",
+ PrimaryButtonText = "Save",
+ SecondaryButtonText = "Don't Save",
+ CloseButtonText = "Cancel"
+ }
+ );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Creates a hyperlink to web pages, files, email addresses, locations in the same page, or anything else a URL can address.
+
+
+
+ <ui:Anchor
+ NavigateUri="https://lepo.co/" />
+
+
+
+
+
+ Control that draws a symmetrical arc with rounded edges.
+
+
+
+ <ui:Arc
+ EndAngle="359"
+ StartAngle="0"
+ Stroke="{ui:ThemeResource SystemAccentColorSecondaryBrush}"
+ StrokeThickness="2"
+ Visibility="Visible" />
+
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the initial angle from which the arc will be drawn.
+
+
+
+
+ Gets or sets the final angle from which the arc will be drawn.
+
+
+
+
+ Gets or sets the direction to where the arc will be drawn.
+
+
+
+
+ Gets a value indicating whether one of the two larger arc sweeps is chosen; otherwise, if is , one of the smaller arc sweeps is chosen.
+
+
+
+
+
+
+
+ Get the geometry that defines this shape.
+ Based on Mark Feldman implementation.
+
+
+
+
+ Draws a point on the coordinates of the given angle.
+ Based on Mark Feldman implementation.
+
+ The angle at which to create the point.
+
+
+
+ Event triggered when one of the key parameters is changed. Forces the geometry to be redrawn.
+
+
+
+ Overrides the default OnRender method to draw the element.
+ A object that is drawn during the rendering pass of this .
+
+
+
+ Represents a text control that makes suggestions to users as they enter text using a keyboard. The app is notified when text has been changed by the user and is responsible for providing relevant suggestions for this control to display.
+
+
+
+ <ui:AutoSuggestBox x:Name="AutoSuggestBox" PlaceholderText="Search">
+ <ui:AutoSuggestBox.Icon>
+ <ui:IconSourceElement>
+ <ui:SymbolIconSource Symbol="Search24" />
+ </ui:IconSourceElement>
+ </ui:AutoSuggestBox.Icon>
+ </ui:AutoSuggestBox>
+
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets your items here if you want to use the default filtering
+
+
+
+
+ Gets or sets a value indicating whether the drop-down portion of the is open.
+
+
+
+
+ Gets or sets the text that is shown in the control.
+
+
+ This property is not typically set in XAML.
+
+
+
+
+ Gets or sets the placeholder text to be displayed in the control.
+
+
+ The placeholder text to be displayed in the control. The default is an empty string.
+
+
+
+
+ Gets or sets the maximum height for the drop-down portion of the control.
+
+
+
+
+ Gets or sets a value indicating whether items in the view will trigger an update of the editable text part of the when clicked.
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets command used for focusing control.
+
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+
+ Occurs when the user submits a search query.
+
+
+
+
+ Event occurs when the user selects an item from the recommended ones.
+
+
+
+
+ Raised after the text content of the editable control component is updated.
+
+
+
+
+
+
+
+ Method for .
+
+ Currently submitted query text.
+
+
+
+ Method for .
+
+ Currently selected item.
+
+
+
+ Method for .
+
+ Data for the text changed event.
+ Changed text.
+
+
+
+ Provides event data for the event.
+
+
+
+
+ Provides data for the event.
+
+
+
+
+ Provides data for the event.
+
+
+
+
+ Provides data for the event.
+
+
+
+
+ The user edited the text.
+
+
+
+
+ The text was changed via code.
+
+
+
+
+ The user selected one of the items in the auto-suggestion box.
+
+
+
+
+ Used to highlight an item, attract attention or flag status.
+
+
+
+ <ui:Badge Appearance="Secondary">
+ <TextBox Text="Hello" />
+ </ui:Badge>
+
+
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+ The control provides the direct path of pages or folders to the current location.
+
+
+
+ <ui:BreadcrumbBar x:Name="BreadcrumbBar" />
+
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets the triggered after clicking
+
+
+
+ Identifies the routed event.
+
+
+
+ Gets or sets custom command executed after selecting the item.
+
+
+
+
+ Occurs when an item is clicked in the .
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Represents an item in a control.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets or sets get or sets margin for the
+
+
+
+
+ Gets or sets a value indicating whether the current item is the last one.
+
+
+
+
+ Gets the Content property value of the BreadcrumbBarItem that is clicked.
+
+
+
+
+ Gets the index of the item that was clicked.
+
+
+
+
+ Inherited from the , adding .
+
+
+
+ <ui:Button
+ Appearance="Primary"
+ Content="WPF UI button with font icon"
+ Icon="{ui:SymbolIcon Symbol=Fluent24}" />
+
+
+ <ui:Button
+ Appearance="Primary"
+ Content="WPF UI button with font icon"
+ Icon="{ui:FontIcon '🌈'}" />
+
+
+
+ The class inherits from the base class.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets displayed .
+
+
+
+
+
+
+
+ Gets or sets background .
+
+
+
+
+ Gets or sets border when the user mouses over the button.
+
+
+
+
+ Gets or sets the foreground when the user clicks the button.
+
+
+
+
+ Gets or sets background when the user clicks the button.
+
+
+
+
+ Gets or sets border when the user clicks the button.
+
+
+
+
+ Gets or sets a value that represents the degree to which the corners of a are rounded.
+
+
+
+
+ Represents a control that allows a user to pick a date from a calendar display.
+
+
+
+ <ui:CalendarDatePicker />
+
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether the current date is highlighted.
+
+
+
+
+ Gets or sets a value indicating whether the calendar view of the is currently shown.
+
+
+
+
+ Gets or sets the day that is considered the beginning of the week.
+
+
+
+
+ Gets or sets the date currently set in the calendar picker.
+
+
+
+
+
+
+
+ Inherited from the interactive card styled according to Fluent Design.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether to display the chevron icon on the right side of the card.
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Ala Pa**one color card.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the main text displayed below the color.
+
+
+
+
+ Gets or sets text displayed under main .
+
+
+
+
+ Gets or sets the font size of .
+
+
+
+
+ Gets or sets the displayed .
+
+
+
+
+ Gets or sets the displayed .
+
+
+
+
+ Gets the displayed in .
+
+
+
+
+ Virtual method triggered when is changed.
+
+
+
+
+ Virtual method triggered when is changed.
+
+
+
+
+ Virtual method triggered when is changed.
+
+
+
+
+ Inherited from the control which displays an additional control on the right side of the card.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets header which is used for each item in the control.
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets or sets the corner radius of the control.
+
+
+
+
+ Inherited from the control which can hide the collapsible content.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets or sets content padding Property
+
+
+
+
+ Simple Card with content and .
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets additional content displayed at the bottom.
+
+
+
+
+ Gets a value indicating whether the has a .
+
+
+
+
+ If you use to extend the UI elements to the non-client area, you can include this container
+ in the template of so that the content inside automatically fills the client area.
+ Using this container can let you get rid of various margin adaptations done in
+ Setter/Trigger of the style of when the window state changes.
+
+
+
+ <Style
+ x:Key="MyWindowCustomStyle"
+ BasedOn="{StaticResource {x:Type Window}}"
+ TargetType="{x:Type controls:FluentWindow}">
+ <Setter Property="Template" >
+ <Setter.Value>
+ <ControlTemplate TargetType="{x:Type Window}">
+ <AdornerDecorator>
+ <controls:ClientAreaBorder
+ Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}">
+ <ContentPresenter x:Name="ContentPresenter" />
+ </controls:ClientAreaBorder>
+ </AdornerDecorator>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+
+
+
+
+ Gets the system value for the padded border thickness () in WPF units.
+
+
+
+
+ Gets the system and values in WPF units.
+
+
+
+
+ Gets the thickness of the window's non-client frame used for maximizing the window with a custom chrome.
+
+
+ If you use a to extend the client area of a window to the non-client area, you need to handle the edge margin issue when the window is maximized.
+ Use this property to get the correct margin value when the window is maximized, so that when the window is maximized, the client area can completely cover the screen client area by no less than a single pixel at any DPI.
+ The method cannot obtain this value directly.
+
+
+
+
+
+
+
+ Represents a control that lets a user pick a color using a color spectrum, sliders, and text input.
+
+
+
+
+ Dialogue displayed inside the application covering its internals, displaying some content.
+
+
+
+ <ContentPresenter x:Name="RootContentDialogPresenter" Grid.Row="0" />
+
+
+ var contentDialog = new ContentDialog(RootContentDialogPresenter);
+
+ contentDialog.SetCurrentValue(ContentDialog.TitleProperty, "Hello World");
+ contentDialog.SetCurrentValue(ContentControl.ContentProperty, "This is a message");
+ contentDialog.SetCurrentValue(ContentDialog.CloseButtonTextProperty, "Close this dialog");
+
+ await contentDialog.ShowAsync(cancellationToken);
+
+
+ var contentDialogService = new ContentDialogService();
+ contentDialogService.SetContentPresenter(RootContentDialogPresenter);
+
+ await _contentDialogService.ShowSimpleDialogAsync(
+ new SimpleContentDialogCreateOptions()
+ {
+ Title = "The cake?",
+ Content = "IS A LIE!",
+ PrimaryButtonText = "Save",
+ SecondaryButtonText = "Don't Save",
+ CloseButtonText = "Cancel"
+ }
+ );
+
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+
+ Gets or sets the title of the .
+
+
+
+
+ Gets or sets the title template of the .
+
+
+
+
+ Gets or sets the width of the .
+
+
+
+
+ Gets or sets the height of the .
+
+
+
+
+ Gets or sets the max width of the .
+
+
+
+
+ Gets or sets the max height of the .
+
+
+
+
+ Gets or sets the margin of the .
+
+
+
+
+ Gets or sets the text to display on the primary button.
+
+
+
+
+ Gets or sets the text to be displayed on the secondary button.
+
+
+
+
+ Gets or sets the text to display on the close button.
+
+
+
+
+ Gets or sets the on the secondary button.
+
+
+
+
+ Gets or sets the on the primary button.
+
+
+
+
+ Gets or sets the on the close button.
+
+
+
+
+ Gets or sets a value indicating whether the primary button is enabled.
+
+
+
+
+ Gets or sets a value indicating whether the secondary button is enabled.
+
+
+
+
+ Gets or sets the to apply to the primary button.
+
+
+
+
+ Gets or sets the to apply to the secondary button.
+
+
+
+
+ Gets or sets the to apply to the close button.
+
+
+
+
+ Gets or sets a value that indicates which button on the dialog is the default action.
+
+
+
+
+ Gets or sets a value indicating whether the footer buttons are visible.
+
+
+
+
+ Gets command triggered after clicking the button in the template.
+
+
+
+
+ Occurs after the dialog is opened.
+
+
+
+
+ Occurs after the dialog starts to close, but before it is closed and before the event occurs.
+
+
+
+
+ Occurs after the dialog is closed.
+
+
+
+
+ Occurs after the has been tapped.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+
+ inside of which the dialogue will be placed. The new will replace the current .
+
+
+
+ Gets or sets inside of which the dialogue will be placed. The new will replace the current .
+
+
+
+
+ Shows the dialog
+
+
+
+
+ Hides the dialog with result
+
+
+
+
+ Occurs after ContentPresenter.Content = null
+
+
+
+
+ Invoked when a is clicked.
+
+ The button that was clicked.
+
+
+
+ Occurs after Loaded event
+
+
+
+
+ Defines constants that specify the default button on a .
+
+
+
+
+ The primary button is the default.
+
+
+
+
+ The secondary button is the default.
+
+
+
+
+ The close button is the default.
+
+
+
+
+ Specifies identifiers to indicate the return value of a .
+
+
+
+
+ No button was tapped.
+
+
+
+
+ The primary button was tapped by the user.
+
+
+
+
+ The secondary button was tapped by the user.
+
+
+
+
+ Overwrites ContextMenu-Style for some UIElements (like RichTextBox) that don't take the default ContextMenu-Style by default.
+ The code inside this CodeBehind-Class forces this ContextMenu-Style on these UIElements through Reflection (because it is only accessible through Reflection it is also only possible through CodeBehind and not XAML)
+
+
+ ContextMenuLoader
+
+
+
+
+ Initializes a new instance of the class and registers editing styles
+ defined in "ContextMenu.xaml" with the .
+
+
+
+
+ InitializeComponent
+
+
+
+
+ Types of the available color accents of the controls.
+
+
+
+
+ Control color according to the current theme accent.
+
+
+
+
+ Control color according to the current theme element.
+
+
+
+
+ Blue color theme.
+
+
+
+
+ Dark color theme.
+
+
+
+
+ Light color theme.
+
+
+
+
+ Red color theme.
+
+
+
+
+ Green color theme.
+
+
+
+
+ Orange color theme.
+
+
+
+
+ Transparent color theme.
+
+
+
+
+ Used to initialize the library controls with static values.
+
+
+
+
+ A DataGrid control that displays data in rows and columns and allows
+ for the entering and editing of data.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the style which is applied to all checkbox column in the DataGrid
+
+
+
+
+ Gets or sets the style for all the column checkboxes in the DataGrid
+
+
+
+
+ A control that drop downs a flyout of choices from which one can be chosen.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the flyout associated with this button.
+
+
+
+
+ Gets or sets a value indicating whether the drop-down for a button is currently open.
+
+
+ if the drop-down is open; otherwise, . The default is .
+
+
+ This method is invoked when the changes.
+ The new value of .
+
+
+
+ Custom with events depending on actions taken by the user.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether the user was recently scrolling in the last few seconds.
+
+
+
+
+ Gets or sets a value indicating whether the user has taken an action related to scrolling.
+
+
+
+
+ Gets or sets additional delay after which the should be hidden.
+
+
+
+
+ Method reporting the mouse entered this element.
+
+
+
+
+ Method reporting the mouse leaved this element.
+
+
+
+
+ Custom with events depending on actions taken by the user.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether the user was scrolling vertically for the last few seconds.
+
+
+
+
+ Gets or sets a value indicating whether the user was scrolling horizontally for the last few seconds.
+
+
+
+
+ Gets or sets the value required for the scroll to show automatically.
+
+
+
+
+ Gets or sets time after which the scroll is to be hidden.
+
+
+
+
+ OnScrollChanged is an override called whenever scrolling state changes on this .
+
+
+ OnScrollChanged fires the ScrollChangedEvent. Overriders of this method should call
+ base.OnScrollChanged(args) if they want the event to be fired.
+
+ ScrollChangedEventArgs containing information about the change in scrolling state.
+
+
+
+ Decides where to put the element.
+
+
+
+
+ Puts the control element on the left.
+
+
+
+
+ Puts the control element on the right.
+
+
+
+
+ Class used to create identifiers of threads or tasks that can be performed multiple times within one instance.
+ represents roughly the time in microseconds at which it was taken.
+
+
+
+
+ Gets or sets the current identifier.
+
+
+
+
+ Creates and gets the next identifier.
+
+
+
+
+ Checks if the identifiers are the same.
+
+
+
+
+ Creates and assigns a random value with an extra time code if possible.
+
+
+
+
+ A custom WinUI Window with more convenience methods.
+
+
+
+
+ Gets contains helper for accessing this window handle.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value determining corner preference for current .
+
+
+
+
+ Gets or sets a value determining preferred backdrop type for current .
+
+
+
+
+ Gets or sets a value indicating whether the default title bar of the window should be hidden to create space for app content.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes static members of the class.
+ Overrides default properties.
+
+
+ Overrides default properties.
+
+
+
+
+
+
+
+ Private property callback.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ Private property callback.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ Private property callback.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ Represents a control that creates a pop-up window that displays information for an element in the interface.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+
+ Gets or sets a value indicating whether a is visible.
+
+
+
+
+ Event triggered when is opened.
+
+
+
+
+ Event triggered when is opened.
+
+
+
+
+ Gets or sets the orientation of the control when the control opens,
+ and specifies the behavior of the
+ control when it overlaps screen boundaries.
+
+
+
+
+ Invoked whenever application code or an internal process,
+ such as a rebuilding layout pass, calls the ApplyTemplate method.
+
+
+
+
+ Defines several predefined text styles that you can apply to some elements responsible for displaying it.
+
+
+
+
+
+ Extends to use Wpf.Ui custom styles
+
+
+ To use this enhanced GridView in a ListView:
+
+ <ListView>
+ <ListView.View>
+ <local:GridView>
+ <GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}"/>
+ <GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
+ </local:GridView>
+ </ListView.View>
+ </ListView>
+
+
+
+
+
+ Extends with MinWidth and MaxWidth properties.
+ It can be used with when in GridView mode.
+
+
+
+ <ui:ListView>
+ <ui:ListView.View>
+ <ui:GridView>
+ <ui:GridViewColumn
+ MinWidth="100"
+ MaxWidth="200"
+ DisplayMemberBinding="{Binding FirstName}"
+ Header="First Name" />
+ </ui:GridView>
+ </ui:ListView.View>
+ </ui:ListView>
+
+
+
+
+
+ Updates the desired width of the column to be clamped between MinWidth and MaxWidth).
+
+
+ Uses reflection to directly set the private `_desiredWidth` field on the `System.Windows.Controls.GridViewColumn`.
+
+
+ Thrown if reflection fails to access the `_desiredWidth` field
+
+
+
+
+ Gets or sets the minimum width of the column.
+
+
+
+ Identifies the dependency property.
+
+
+
+ gets or sets the maximum width of the column.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Extends , and adds layout support for , which can have and .
+
+
+
+
+ Extends , and adds header row layout support for , which can have and .
+
+
+
+
+ Button that opens a URL in a web browser.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the URL (or application shortcut) to open.
+
+
+
+
+ UI with attributes.
+
+
+
+
+ Gets or sets the of the control, if available.
+
+
+
+
+ Represents an icon that uses a glyph from the specified font.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Gets or sets the character code that identifies the icon glyph.
+
+ The hexadecimal character code for the icon glyph.
+
+
+
+ Represents the base class for an icon UI element.
+
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+ Coerces the value of an Icon dependency property, allowing the use of either IconElement or IconSourceElement.
+
+ The dependency object (unused).
+ The value to be coerced.
+ An IconElement, either directly or derived from an IconSourceElement.
+
+
+
+ Tries to convert and to .
+
+
+
+
+ Represents an icon that uses an IconSource as its content.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets
+
+
+
+
+ Represents an icon that uses an as its content.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the Source on this Image.
+
+
+
+
+ Represents a text element containing an icon glyph.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets or sets a value indicating whether or not we should use the .
+
+
+
+
+ Represents an icon source that uses a glyph from the specified font.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Gets or sets the character code that identifies the icon glyph.
+
+ The hexadecimal character code for the icon glyph.
+
+
+
+ Represents the base class for an icon source.
+
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+ Represents an icon source that uses a glyph from the specified font.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+
+
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets or sets a value indicating whether or not we should use the .
+
+
+
+
+ The control that should react to changes in the screen DPI.
+
+
+
+
+ Control that allows you to set an icon in it with an .
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Represents an image with additional properties for Borders and Rounded corners
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the Source on this Image.
+ The Source property is the ImageSource that holds the actual image drawn.
+
+
+
+
+ Gets or sets the Stretch on this Image.
+ The Stretch property determines how large the Image will be drawn.
+
+
+
+
+ Gets or sets the stretch direction of the Viewbox, which determines the restrictions on
+ scaling that are applied to the content inside the Viewbox. For instance, this property
+ can be used to prevent the content from being smaller than its native size or larger than
+ its native size.
+
+
+
+
+ Gets or sets the CornerRadius property allows users to control the roundness of the corners independently by
+ setting a radius value for each corner. Radius values that are too large are scaled so that they
+ smoothly blend from corner to corner.
+
+
+
+
+ Gets the CornerRadius for the inner image's Mask.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the title of the .
+
+
+
+
+ Gets or sets the title of the .
+
+
+
+
+ Gets or sets the title of the .
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Communicates that the InfoBadge is displaying general information that requires the user's attention.
+
+
+
+
+ Communicates that the InfoBadge is displaying general information that requires the user's attention.
+
+
+
+
+ Communicates that the InfoBadge is displaying general information that requires the user's attention.
+
+
+
+
+ Communicates that the InfoBadge is displaying general information that requires the user's attention.
+
+
+
+
+ Communicates that the InfoBadge is displaying general information that requires the user's attention.
+
+
+
+
+ An is an inline notification for essential app-
+ wide messages. The InfoBar will take up space in a layout and will not
+ cover up other content or float on top of it. It supports rich content
+ (including titles, messages, and icons) and can be configured to be
+ user-dismissable or persistent.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether the user can close the . Defaults to true.
+
+
+
+
+ Gets or sets a value indicating whether the is open.
+
+
+
+
+ Gets or sets the title of the .
+
+
+
+
+ Gets or sets the message of the .
+
+
+
+
+ Gets or sets the type of the to apply
+ consistent status color, icon, and assistive technology settings
+ dependent on the criticality of the notification.
+
+
+
+
+ Gets the triggered after clicking
+ the close button.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Communicates that the InfoBar is displaying general information that requires the user's attention.
+
+
+
+
+ Communicates that the InfoBar is displaying information regarding a long-running and/or background task
+ that has completed successfully.
+
+
+
+
+ Communicates that the InfoBar is displaying information regarding a condition that might cause a problem in
+ the future.
+
+
+
+
+ Communicates that the InfoBar is displaying information regarding an error or problem that has occurred.
+
+
+
+
+ Items range.
+ Based on .
+
+
+
+
+ Control changing its properties or appearance depending on the theme.
+
+
+
+
+ Gets the theme that is currently set.
+
+
+
+
+ Extends , and adds customized support or .
+
+
+
+ <ui:ListView ItemsSource="{Binding ...}" >
+ <ui:ListView.View>
+ <ui:GridView>
+ <GridViewColumn
+ DisplayMemberBinding="{Binding FirstName}"
+ Header="First Name" />
+ <GridViewColumn
+ DisplayMemberBinding="{Binding LastName}"
+ Header="Last Name" />
+ </ui:GridView>
+ </ui:ListView.View>
+ </ui:ListView>
+
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the view state of the , enabling custom logic based on the current view.
+
+ The current view state of the .
+
+
+
+ Prestyled loading screen with .
+
+
+
+
+ Extended with properties.
+
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Changes readonly field value of to false.
+
+
+ MenuLoader
+
+
+
+
+ Initializes a new instance of the class.
+
+
+ Sets menu alignment on initialization.
+
+
+
+
+ InitializeComponent
+
+
+
+
+ Customized window for notifications.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether to show the in .
+
+
+
+
+ Gets or sets the text to display on the primary button.
+
+
+
+
+ Gets or sets the text to be displayed on the secondary button.
+
+
+
+
+ Gets or sets the text to display on the close button.
+
+
+
+
+ Gets or sets the on the primary button
+
+
+
+
+ Gets or sets the on the secondary button
+
+
+
+
+ Gets or sets the on the close button
+
+
+
+
+ Gets or sets the on the primary button
+
+
+
+
+ Gets or sets the on the secondary button
+
+
+
+
+ Gets or sets the on the close button
+
+
+
+
+ Gets or sets a value indicating whether the primary button is enabled.
+
+
+
+
+ Gets or sets a value indicating whether the secondary button is enabled.
+
+
+
+
+ Gets the command triggered after clicking the button on the Footer.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Displays a message box
+
+
+ Thrown if the operation is canceled.
+
+
+
+ Occurs after Loading event
+
+
+
+
+ Resizes the MessageBox to fit the content's size, including margins.
+
+ The root element of the MessageBox
+
+
+
+ Occurs after the is clicked
+
+ The MessageBox button
+
+
+
+ Defines constants that specify the default button on a .
+
+
+
+
+ The primary button
+
+
+
+
+ The secondary button
+
+
+
+
+ The close button
+
+
+
+
+ Specifies identifiers to indicate the return value of a .
+
+
+
+
+ No button was tapped.
+
+
+
+
+ The primary button was tapped by the user.
+
+
+
+
+ The secondary button was tapped by the user.
+
+
+
+
+ Represents a container that enables navigation of app content. It has a header, a view for the main content, and a menu pane for navigation commands.
+
+
+
+
+ Gets or sets the header content.
+
+
+
+
+ Gets or sets the visibility.
+
+
+
+
+ Gets or sets a value indicating whether the header is always visible.
+
+
+
+
+ Gets the collection of menu items displayed in the NavigationView.
+
+
+
+
+ Gets or sets an object source used to generate the content of the NavigationView menu.
+
+
+
+
+ Gets the list of objects to be used as navigation items in the footer menu.
+
+
+
+
+ Gets or sets the object that represents the navigation items to be used in the footer menu.
+
+
+
+
+ Gets the selected item.
+
+
+
+
+ Gets or sets a UI element that is shown at the top of the control, below the pane if PaneDisplayMode is Top.
+
+
+
+
+ Gets a value indicating whether the back button is enabled or disabled.
+
+
+
+
+ Gets or sets a value that indicates whether the back button is visible or not.
+ Default value is "Auto", which indicates that button visibility depends on the DisplayMode setting of the NavigationView.
+
+
+
+
+ Gets or sets a value indicating whether the toggle button is visible.
+
+
+
+
+ Gets or sets a value indicating whether the NavigationView pane is expanded to its full width.
+
+
+
+
+ Gets or sets a value indicating whether the pane is shown.
+
+
+
+
+ Gets or sets the width of the NavigationView pane when it's fully expanded.
+
+
+
+
+ Gets or sets the width of the NavigationView pane in its compact display mode.
+
+
+
+
+ Gets or sets the content for the pane header.
+
+
+
+
+ Gets or sets the label adjacent to the menu icon when the NavigationView pane is open.
+
+
+
+
+ Gets or sets the content for the pane footer.
+
+
+
+
+ Gets or sets a value that specifies how the pane and content areas of a NavigationView are being shown.
+ It is not the same PaneDisplayMode as in WinUi.
+
+
+
+
+ Gets or sets an TitleBar to be displayed in the NavigationView.
+
+
+
+
+ Gets or sets an AutoSuggestBox to be displayed in the NavigationView.
+
+
+
+
+ Gets or sets an BreadcrumbBar that is in .
+
+
+
+
+ Gets or sets the template property for and .
+
+
+
+
+ Gets or sets a value deciding how long the effect of the transition between the pages should take.
+
+
+
+
+ Gets or sets type of transitions during navigation.
+
+
+
+
+ Gets or sets margin for a Frame of
+
+
+
+
+ Occurs when the NavigationView pane is opened.
+
+
+
+
+ Occurs when the NavigationView pane is closed.
+
+
+
+
+ Occurs when the currently selected item changes.
+
+
+
+
+ Occurs when an item in the menu receives an interaction such as a click or tap.
+
+
+
+
+ Occurs when the back button receives an interaction such as a click or tap.
+
+
+
+
+ Occurs when a new navigation is requested
+
+
+
+
+ Occurs when navigated to page
+
+
+
+
+ Gets a value indicating whether there is at least one entry in back navigation history.
+
+
+
+
+ Synchronously navigates current navigation Frame to the
+ given Element.
+
+
+
+
+ Synchronously navigates current navigation Frame to the
+ given Element.
+
+
+
+
+ Synchronously adds an element to the navigation stack and navigates current navigation Frame to the
+
+
+
+
+ Replaces the contents of the navigation frame, without changing the currently selected item or triggering an .
+
+
+
+
+ Replaces the contents of the navigation frame, without changing the currently selected item or triggering an .
+
+
+
+
+ Navigates the NavigationView to the next journal entry.
+
+ if successfully navigated forward, otherwise .
+
+
+
+ Navigates the NavigationView to the previous journal entry.
+
+ if successfully navigated backward, otherwise .
+
+
+
+ Clears the NavigationView history.
+
+
+
+
+ Allows you to assign to the NavigationView a special service responsible for retrieving the page instances.
+
+
+
+
+ Allows you to assign a general to the NavigationView that will be used to retrieve page instances and view models.
+
+
+
+
+ Represents the container for an item in a NavigationView control.
+
+
+
+
+ Gets the unique identifier that allows the item to be located in the navigation.
+
+
+
+
+ Gets or sets the content
+
+
+
+
+ Gets or sets the icon displayed in the MenuItem object.
+
+
+
+
+ Gets the collection of menu items displayed in the NavigationView.
+
+
+
+
+ Gets or sets an object source used to generate the content of the NavigationView menu.
+
+
+
+
+ Gets a value indicating whether the current element is active.
+
+
+
+
+ Gets or sets a value indicating whether the sub- are expanded.
+
+
+
+
+ Gets or sets the unique tag used by the parent navigation system for the purpose of searching and navigating.
+
+
+
+
+ Gets or sets the type of the page to be navigated. (Should be derived from ).
+
+
+
+
+ Gets or sets the caching characteristics for a page involved in a navigation.
+
+
+
+
+ Gets or sets the template property
+
+
+
+
+ Gets or sets the parent if it's in collection
+
+
+
+
+ Add / Remove ClickEvent handler.
+
+
+
+
+ Correctly activates
+
+
+
+
+ Correctly deactivates
+
+
+
+
+ Specifies caching characteristics for a page involved in a navigation.
+
+
+
+
+ The page is never cached and a new instance of the page is created on each visit.
+
+
+
+
+ The page is cached, but the cached instance is discarded when the size of the cache for the frame is exceeded.
+
+
+
+
+ The page is cached and the cached instance is reused for every visit regardless of the cache size for the frame.
+
+
+
+
+ Defines attached properties for .
+
+
+ Represents a container that enables navigation of app content. It has a header, a view for the main content, and a menu pane for navigation commands.
+
+
+ Defines events for .
+
+
+ Defines navigation logic and state management for .
+
+
+ Defines the dependency properties and dp callbacks for control
+
+
+ Defines the template parts for the control
+
+
+
+ Registers attached property NavigationView.HeaderContent
+
+
+ Helper for getting from .
+ to read from.
+ HeaderContent property value.
+
+
+ Helper for setting on .
+ to set on.
+ HeaderContent property value.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets the parent for its children.
+
+
+
+ Helper for getting from .
+ to read from.
+ NavigationParent property value.
+
+
+
+ Initializes static members of the class and overrides default property metadata.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+
+
+
+
+
+
+ This virtual method is called when this element is detached form a loaded tree.
+
+
+
+
+ This virtual method is called when ActualWidth or ActualHeight (or both) changed.
+
+
+
+
+ This virtual method is called when is clicked.
+
+
+
+
+ This virtual method is called when is clicked.
+
+
+
+
+ This virtual method is called when is clicked.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ Navigate to the page after its name is selected in .
+
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Raises the pane opened event.
+
+
+
+
+ Raises the pane closed event.
+
+
+
+
+ Raises the selection changed event.
+
+
+
+
+ Raises the item invoked event.
+
+
+
+
+ Raises the back requested event.
+
+
+
+
+ Raises the navigating requested event.
+
+
+
+
+ Raises the navigated requested event.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether debugging messages for this control are enabled
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Template element represented by the PART_MenuItemsItemsControl name.
+
+
+
+
+ Template element represented by the PART_MenuItemsItemsControl name.
+
+
+
+
+ Template element represented by the PART_FooterMenuItemsItemsControl name.
+
+
+
+
+ Template element represented by the PART_BackButton name.
+
+
+
+
+ Template element represented by the PART_ToggleButton name.
+
+
+
+
+ Template element represented by the PART_AutoSuggestBoxSymbolButton name.
+
+
+
+
+ Gets or sets the control responsible for rendering the content.
+
+
+
+
+ Gets or sets the control located at the top of the pane with left arrow icon.
+
+
+
+
+ Gets or sets the control located at the top of the pane with hamburger icon.
+
+
+
+
+ Gets or sets the control located at the top of the pane with left arrow icon.
+
+
+
+
+ Gets or sets the control located at the top of the pane with hamburger icon.
+
+
+
+
+ Gets or sets the control that is visitable if PaneDisplayMode="Left" and in compact state
+
+
+
+
+
+
+
+ Internal activator for creating content instances of the navigation view items.
+
+
+
+
+ Creates new instance of type derived from .
+
+ to instantiate.
+ Additional context to set.
+ Instance of the object or .
+
+
+
+ Defines constants that specify whether the back button is visible in NavigationView.
+
+
+
+
+ Do not display the back button in NavigationView, and do not reserve space for it in layout.
+
+
+
+
+ Display the back button in NavigationView.
+
+
+
+
+ The system chooses whether or not to display the back button, depending on the device/form factor. On phones, tablets, desktops, and hubs, the back button is visible. On Xbox/TV, the back button is collapsed.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets type of transitions during navigation.
+
+
+
+
+ Gets or sets a value indicating whether the dynamic scroll viewer is enabled.
+
+
+
+
+ Represents the container for an item in a NavigationView control.
+ When needed, it can be used as a normal button with a action.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+
+
+
+ Gets a value indicating whether MenuItems.Count > 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Correctly activates
+
+
+
+
+ Correctly deactivates
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Is called when mouse is clicked down.
+
+
+
+
+ Represents a header for a group of menu items in a NavigationMenu.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the text presented in the header element.
+
+
+
+
+ Gets or sets the icon.
+
+
+
+
+ Represents a line that separates menu items in a NavigationMenu.
+
+
+
+
+ Defines constants that specify how and where the NavigationView pane is shown.
+
+
+
+
+ The pane is shown on the left side of the control.
+
+
+
+
+ The pane is shown on the left side of the control. Only the pane icons are shown.
+
+
+
+
+ The pane is shown on the left side of the control. Large icons with titles underneath are the only display option. Does not support .
+ Similar to the Windows Store (2022) app.
+
+
+
+
+ The pane is shown at the top of the control.
+
+
+
+
+ The pane is shown at the bottom of the control.
+
+
+
+
+ An interface that returns a string representation of a provided value, using distinct format methods to format several data types.
+
+
+
+
+ Returns a string representation of a value.
+
+
+
+
+ Returns a string representation of an value.
+
+
+
+
+ Returns a string representation of a value.
+
+
+
+
+ An interface that parses a string representation of a numeric value.
+
+
+
+
+ Attempts to parse a string representation of a numeric value.
+
+
+
+
+ Attempts to parse a string representation of an numeric value.
+
+
+
+
+ Attempts to parse a string representation of an numeric value.
+
+
+
+
+ Represents a control that can be used to display and edit numbers.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+
+ Gets or sets the numeric value of a .
+
+
+
+
+ Gets or sets the number of decimal places to be rounded when converting from Text to Value.
+
+
+
+
+ Gets or sets the value that is added to or subtracted from when a small change is made, such as with an arrow key or scrolling.
+
+
+
+
+ Gets or sets the value that is added to or subtracted from when a large change is made, such as with the PageUP and PageDown keys.
+
+
+
+
+ Gets or sets the numerical maximum for .
+
+
+
+
+ Gets or sets the numerical minimum for .
+
+
+
+
+ Gets or sets a value indicating whether the control will accept and evaluate a basic formulaic expression entered as input.
+
+
+
+
+ Gets or sets the number formatter.
+
+
+
+
+ Gets or sets a value that indicates the placement of buttons used to increment or decrement the property.
+
+
+
+
+ Gets or sets the input validation behavior to invoke when invalid input is entered.
+
+
+
+
+ Occurs after the user triggers evaluation of new input by pressing the Enter key, clicking a spin button, or by changing focus.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Is called when in this changes.
+
+
+
+
+ Is called when something is pasted in this .
+
+
+
+
+ Defines values that specify how the spin buttons used to increment or decrement the are displayed.
+
+
+
+
+ The spin buttons are not displayed.
+
+
+
+
+ The spin buttons have two visual states, depending on focus. By default, the spin buttons are displayed in a compact, vertical orientation. When the Numberbox gets focus, the spin buttons expand.
+
+
+
+
+ The spin buttons are displayed in an expanded, horizontal orientation.
+
+
+
+
+ Defines values that specify the input validation behavior of a when invalid input is entered.
+
+
+
+
+ Input validation is disabled.
+
+
+
+
+ Invalid input is replaced by PlaceholderText text.
+
+
+
+
+ Base nubmer formatter that uses default format specifier and that represents the culture used by the current thread.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ A custom ScrollViewer that allows certain mouse events to bubble through when it's inactive.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether blocked inner scrolling should be propagated forward.
+
+
+
+
+ The modified password control.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+
+ Gets or sets currently typed text represented by asterisks.
+
+
+
+
+ Gets or sets character used to mask the password.
+
+
+
+
+ Gets a value indicating whether the password is revealed.
+
+
+
+
+ Gets or sets a value indicating whether to display the password reveal button.
+
+
+
+
+ Event fired from this text box when its inner content
+ has been changed.
+
+
+ It is redirected from inner TextContainer.Changed event.
+
+
+
+
+
+
+
+ Is called when property is changing.
+
+
+
+
+ Is called when property is changing.
+
+
+
+
+ Triggered by clicking a button in the control template.
+
+ Additional parameters.
+
+
+
+ Called when is changed.
+
+
+
+
+ Called if the character is changed in the during the run.
+
+
+
+
+ Called if the reveal mode is changed in the during the run.
+
+
+
+
+ Rotating loading ring.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the progress.
+
+
+
+
+ Gets or sets a value indicating whether shows actual values ()
+ or generic, continuous progress feedback.
+
+
+
+
+ Gets or sets the .
+
+
+
+
+ Gets the when is .
+
+
+
+
+ Gets background ring fill.
+
+
+
+
+ Gets background ring visibility.
+
+
+
+
+ Re-draws depending on .
+
+
+
+
+ Validates the entered and redraws the .
+
+
+
+
+ Displays the rating scale with interactions.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+
+ Gets or sets the rating value.
+
+
+
+
+ Gets or sets the maximum allowed rating value.
+
+
+
+
+ Gets or sets a value indicating whether half of the star can be selected.
+
+
+
+
+ Occurs after the user selects the rating.
+
+
+
+
+ Is called when changes.
+
+
+
+
+ Is called when mouse is moved away from the control.
+
+
+
+
+ Is called when mouse is moved around the control.
+
+
+
+
+ Is called when mouse is cliked down.
+
+
+
+
+ Adjusts the control's in response to keyboard input, incrementing or decrementing based on the key pressed.
+
+ Key event arguments containing details about the key press.
+
+
+
+ Is called when Template is changed.
+
+
+
+
+ Extends the control with additional properties.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value indicating whether the user can select text in the control.
+
+
+
+
+ Direction of .
+ Based on .
+
+
+
+
+ Vertical scroll direction.
+
+
+
+
+ Horizontal scroll direction.
+
+
+
+
+ Snackbar inform user of a process that an app has performed or will perform. It appears temporarily, towards the bottom of the window.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+
+ Gets or sets a value indicating whether the close button should be visible.
+
+
+
+
+ Gets or sets the transform.
+
+
+
+
+ Gets or sets a value indicating whether the is visible.
+
+
+
+
+ Gets or sets a time for which the should be visible.
+
+
+
+
+ Gets or sets the title of the .
+
+
+
+
+ Gets or sets the title template of the .
+
+
+
+
+ Gets or sets the icon
+
+
+
+
+
+
+
+ Gets or sets the foreground of the .
+
+
+
+
+ Gets the command triggered after clicking the button in the template.
+
+
+
+
+ Occurs when the snackbar is about to open.
+
+
+
+
+ Occurs when the snackbar is about to close.
+
+
+
+
+ Initializes a new instance of the class with a specified presenter.
+
+ The to manage the snackbar's display and interactions.
+
+
+
+ Shows the
+
+
+
+
+ Shows the
+
+
+
+
+ Shows the .
+
+
+
+
+ Shows the .
+
+
+
+
+ Hides the
+
+
+
+
+ This virtual method is called when is opening and it raises the .
+
+
+
+
+ This virtual method is called when is closing and it raises the .
+
+
+
+
+ Specifies how remaining space is distributed.
+ Based on .
+
+
+
+
+ Spacing is disabled and all items will be arranged as closely as possible.
+
+
+
+
+ The remaining space is evenly distributed between the items on a layout row, as well as the start and end of each row.
+
+
+
+
+ The remaining space is evenly distributed between the items on a layout row, excluding the start and end of each row.
+
+
+
+
+ The remaining space is evenly distributed between start and end of each row.
+
+
+
+
+ Represents a button with two parts that can be invoked separately. One part behaves like a standard button and the other part invokes a flyout.
+
+
+
+
+ Template element represented by the ToggleButton name.
+
+
+
+
+ Gets or sets control responsible for toggling the drop-down button.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the flyout associated with this button.
+
+
+
+
+ Gets or sets a value indicating whether the drop-down for a button is currently open.
+
+
+ if the drop-down is open; otherwise, . The default is .
+
+
+ This method is invoked when the changes.
+ The new value of .
+
+
+ This method is invoked when the changes.
+ The new value of .
+
+
+
+
+
+
+ Triggered when the control is unloaded. Releases resource bindings.
+
+
+
+
+ Represents a list of filled Fluent System Icons v.1.1.233.
+ May be converted to using GetGlyph() or to using GetString()
+
+
+
+
+ Actually, this icon is not empty, but makes it easier to navigate.
+
+
+
+
+ Set of static methods to operate on and .
+
+
+
+
+ If the icon is not found in some places, this one will be displayed.
+
+
+
+
+ If the filled icon is not found in some places, this one will be displayed.
+
+
+
+
+ Finds icon based on name.
+
+ Name of the icon.
+
+
+
+ Finds icon based on name.
+
+ Name of the icon.
+
+
+
+ Represents a list of regular Fluent System Icons v.1.1.233.
+ May be converted to using GetGlyph() or to using GetString()
+
+
+
+
+ Actually, this icon is not empty, but makes it easier to navigate.
+
+
+
+
+ The TabView control is a way to display a set of tabs and their respective content.
+ Tab controls are useful for displaying several pages (or documents) of content while
+ giving a user the capability to rearrange, open, or close new tabs.
+
+
+
+
+ Represents a single tab within a .
+
+
+
+
+ Extended with additional parameters like .
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the of the text.
+
+
+
+
+ Gets or sets the color of the text.
+
+
+
+
+ Extended with additional parameters like .
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Gets or sets which side the icon should be placed on.
+
+
+
+
+ Gets or sets placeholder text.
+
+
+
+
+ Gets or sets a value indicating whether to enable the placeholder text.
+
+
+
+
+ Gets or sets a value indicating whether to display the placeholder text.
+
+
+
+
+ Gets or sets a value indicating whether to enable the clear button.
+
+
+
+
+ Gets or sets a value indicating whether to show the clear button when is focused.
+
+
+
+
+ Gets or sets a value indicating whether text selection is enabled.
+
+
+
+
+ Gets the command triggered when clicking the button.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Reveals the clear button by property.
+
+
+
+
+ Hides the clear button by property.
+
+
+
+
+ Triggered when the user clicks the clear text button.
+
+
+
+
+ Triggered by clicking a button in the control template.
+
+
+
+
+ Colors for UI labels and static text
+
+
+
+
+ Rest or Hover
+
+
+
+
+ Rest or Hover
+
+
+
+
+ Pressed only (not accessible)
+
+
+
+
+ Disabled only (not accessible)
+
+
+
+
+ Allows to rate positively or negatively by clicking on one of the thumbs.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+
+ Occurs when is changed.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the value determining the current state of the control.
+
+
+
+
+ Gets the command triggered when clicking the button.
+
+
+
+
+ Initializes a new instance of the class and attaches .
+
+
+
+
+ Triggered by clicking a button in the control template.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ States of the control.
+
+
+
+
+ No thumb has been clicked.
+
+
+
+
+ The thumb up has been clicked.
+
+
+
+
+ The thumb down has been clicked.
+
+
+
+
+ Clock system.
+
+
+
+
+ Represents a control that allows a user to pick a time value.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the content for the control's header.
+
+
+
+
+ Gets or sets the time currently set in the time picker.
+
+
+
+
+ Gets or sets the time currently selected in the time picker
+
+
+
+
+ Gets or sets a value that indicates the time increments shown in the minute picker.
+ For example, 15 specifies that the TimePicker minute control displays only the choices 00, 15, 30, 45.
+
+
+
+
+ Gets or sets the clock system to use.
+
+
+
+
+ Custom navigation buttons for the window.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the routed event.
+
+
+ Identifies the dependency property.
+
+
+
+
+
+
+ Gets or sets title displayed on the left.
+
+
+
+
+ Gets or sets the content displayed in the .
+
+
+
+
+ Gets or sets the foreground of the navigation buttons.
+
+
+
+
+ Gets or sets the background of the navigation buttons when hovered.
+
+
+
+
+ Gets a value indicating whether the current window is maximized.
+
+
+
+
+ Gets or sets a value indicating whether the controls affect main application window.
+
+
+
+
+ Gets or sets a value indicating whether to show the maximize button.
+
+
+
+
+ Gets or sets a value indicating whether to show the minimize button.
+
+
+
+
+ Gets or sets a value indicating whether to show the help button
+
+
+
+
+ Gets or sets a value indicating whether to show the close button.
+
+
+
+
+ Gets or sets a value indicating whether the maximize functionality is enabled. If disabled the MaximizeActionOverride action won't be called
+
+
+
+
+ Gets or sets the titlebar icon.
+
+
+
+
+ Gets or sets a value indicating whether the window can be closed by double clicking on the icon
+
+
+
+
+ Event triggered after clicking close button.
+
+
+
+
+ Event triggered after clicking maximize or restore button.
+
+
+
+
+ Event triggered after clicking minimize button.
+
+
+
+
+ Event triggered after clicking help button
+
+
+
+
+ Gets the command triggered when clicking the titlebar button.
+
+
+
+
+ Gets or sets the that should be executed when the Maximize button is clicked."/>
+
+
+
+
+ Gets or sets what should be executed when the Minimize button is clicked.
+
+
+
+
+ Initializes a new instance of the class and sets the default event.
+
+
+
+
+
+
+
+ Invoked whenever application code or an internal process,
+ such as a rebuilding layout pass, calls the ApplyTemplate method.
+
+
+
+
+ This virtual method is triggered when the app's theme changes.
+
+
+
+
+ Listening window hooks after rendering window content to SizeToContent support
+
+
+
+
+ Show 'SystemMenu' on mouse right button up.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the type of the button.
+
+
+
+
+ Gets or sets the foreground of the navigation buttons.
+
+
+
+
+ Gets or sets the foreground of the navigation buttons when moused over.
+
+
+
+
+ Forces button background to change.
+
+
+
+
+ Forces button background to change.
+
+
+
+
+ Invokes click on the button.
+
+
+
+
+ Type of the Title Bar button.
+
+
+
+
+ Unknown button.
+
+
+
+
+ Maximize button.
+
+
+
+
+ Close button.
+
+
+
+
+ Maximize button.
+
+
+
+
+ Restore button.
+
+
+
+
+ Help button.
+
+
+
+
+ Use to present users with two mutally exclusive options (like on/off).
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the content that should be displayed when the is in the "Off" state.
+
+
+
+
+ Gets or sets the content that should be displayed when the is in the "On" state.
+
+
+
+
+ TODO: Work in progress.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the data used to generate the child elements of this control.
+
+
+
+
+ Work in progress.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the title that will be displayed.
+
+
+
+
+ Gets or sets the column group name.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ Work in progress.
+
+
+
+
+ Extended with properties.
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets displayed .
+
+
+
+
+ Represents a method that handles general events.
+
+ The type of the sender.
+ The type of the event data.
+ The source of the event.
+ An object that contains the event data.
+
+
+
+ Simple control that displays a gird of items. Depending on the orientation, the items are either stacked horizontally or vertically
+ until the items are wrapped to the next row or column. The control is using virtualization to support large amount of items.
+ In order to work properly all items must have the same size.
+ Based on .
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets a value that specifies the orientation in which items are arranged. The default value is .
+
+
+
+
+ Gets or sets the spacing mode used when arranging the items. The default value is .
+
+
+
+
+ Gets or sets a value indicating whether the items get stretched to fill up remaining space. The default value is false.
+
+
+ The MaxWidth and MaxHeight properties of the ItemContainerStyle can be used to limit the stretching.
+ In this case the use of the remaining space will be determined by the SpacingMode property.
+
+
+
+
+ Initializes the with .
+
+
+
+
+ Virtualized .
+ Based on .
+
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the cache length unit.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Work in progress.
+
+
+
+
+ Base abstract class for creating virtualized panels.
+ Based on .
+
+
+
+
+ Owner of the displayed items.
+
+
+
+
+ Items generator.
+
+
+
+
+ Previously set visibility of the vertical scroll bar.
+
+
+
+
+ Previously set visibility of the horizontal scroll bar.
+
+
+
+
+
+
+
+ Gets the scroll unit.
+
+
+
+
+ Gets or sets the direction in which the panel scrolls when user turns the mouse wheel.
+
+
+
+
+ Gets a value indicating whether the virtualizing is enabled.
+
+
+
+
+ Gets the virtualization mode.
+
+
+
+
+ Gets a value indicating whether the panel is in VirtualizationMode.Recycling.
+
+
+
+
+ Gets the cache length before and after the viewport.
+
+
+
+
+ Gets the Unit of the cache length. Can be Pixel, Item or Page.
+ When the ItemsOwner is a group item it can only be pixel or item.
+
+
+
+
+ Gets the ItemsControl (e.g. ListView).
+
+
+
+
+ Gets the ItemsControl (e.g. ListView) or if the ItemsControl is grouping a GroupItem.
+
+
+
+
+ Gets items collection.
+
+
+
+
+ Gets the offset.
+
+
+
+
+ Gets items container.
+
+
+
+
+ Gets or sets the range of items that a realized in or cache.
+
+
+
+
+ Gets the .
+
+
+
+
+ Gets the viewport.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the scroll owner.
+
+
+
+
+ Gets or sets a value indicating whether the content can be vertically scrolled.
+
+
+
+
+ Gets or sets a value indicating whether the content can be horizontally scrolled.
+
+
+
+
+ Gets or sets the scroll line delta for pixel based scrolling. The default value is 16 dp.
+
+
+
+
+ Gets or sets the mouse wheel delta for pixel based scrolling. The default value is 48 dp.
+
+
+
+
+ Gets or sets the scroll line delta for item based scrolling. The default value is 1 item.
+
+
+
+
+ Gets or sets the mouse wheel delta for item based scrolling. The default value is 3 items.
+
+
+
+
+ Gets width of the .
+
+
+
+
+ Gets height of the .
+
+
+
+
+ Gets the horizontal offset.
+
+
+
+
+ Gets the vertical offset.
+
+
+
+
+ Gets the width.
+
+
+
+
+ Gets the height.
+
+
+
+
+
+
+
+ Sets the vertical offset.
+
+
+
+
+ Sets the horizontal offset.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Updates scroll offset, extent and viewport.
+
+
+
+
+ Gets item index from the generator.
+
+
+
+
+ Gets the position of children from the generator.
+
+
+
+
+
+
+
+ Realizes visible and cached items.
+
+
+
+
+ Virtualizes (cleanups) no longer visible or cached items.
+
+
+
+
+ Sets vertical scroll offset by given amount.
+
+ The value by which the offset is to be increased.
+
+
+
+ Sets horizontal scroll offset by given amount.
+
+ The value by which the offset is to be increased.
+
+
+
+ Calculates the extent that would be needed to show all items.
+
+
+
+
+ Calculates the item range that is visible in the viewport or cached.
+
+
+
+
+ Gets line up scroll amount.
+
+
+
+
+ Gets line down scroll amount.
+
+
+
+
+ Gets line left scroll amount.
+
+
+
+
+ Gets line right scroll amount.
+
+
+
+
+ Gets mouse wheel up scroll amount.
+
+
+
+
+ Gets mouse wheel down scroll amount.
+
+
+
+
+ Gets mouse wheel left scroll amount.
+
+
+
+
+ Gets mouse wheel right scroll amount.
+
+
+
+
+ Gets page up scroll amount.
+
+
+
+
+ Gets page down scroll amount.
+
+
+
+
+ Gets page left scroll amount.
+
+
+
+
+ Gets page right scroll amount.
+
+
+
+
+ Extended base class for .
+ Based on .
+
+
+
+
+ Gets or sets the size of the single child element.
+
+
+
+
+ Gets or sets the amount of the displayed rows.
+
+
+
+
+ Gets or sets the amount of displayed items per row.
+
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+ Identifies the dependency property.
+
+
+
+ Gets or sets the spacing mode used when arranging the items. The default value is .
+
+
+
+
+ Gets or sets a value that specifies the orientation in which items are arranged. The default value is .
+
+
+
+
+ Gets or sets a value that specifies the size of the items. The default value is .
+ If the value is the size of the items gots measured by the first realized item.
+
+
+
+
+ Gets or sets a value indicating whether the items get stretched to fill up remaining space. The default value is false.
+
+
+ The MaxWidth and MaxHeight properties of the ItemContainerStyle can be used to limit the stretching.
+ In this case the use of the remaining space will be determined by the SpacingMode property.
+
+
+
+
+ This virtual method is called when is changed.
+
+
+
+
+ Private callback for .
+
+
+
+
+
+
+
+ Updates child size of .
+
+
+
+
+ Calculates child size.
+
+
+
+
+
+
+
+ Calculates desired spacing between items.
+
+
+
+
+
+
+
+ Calculates desired child arrange size.
+
+
+
+
+ Gets the style property value for item containers within the .
+
+ The expected type of the property value.
+ The to retrieve the value for.
+ The value to return if the property is not set.
+ The value of the specified property if found; otherwise, the .
+
+
+
+
+
+
+ Gets item row index.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Gets X panel orientation.
+
+
+
+
+ Gets Y panel orientation.
+
+
+
+
+ Gets panel width.
+
+
+
+
+ Gets panel height.
+
+
+
+
+ Defines panel size.
+
+
+
+
+ Defines panel coordinates and size.
+
+
+
+
+ Applies the chosen backdrop effect to the selected window.
+
+
+
+
+ Checks whether the selected backdrop type is supported on current platform.
+
+ if the selected backdrop type is supported on current platform.
+
+
+
+ Applies a backdrop effect to the selected .
+
+ The window to which the backdrop effect will be applied.
+ The type of backdrop effect to apply. Determines the visual appearance of the window's backdrop.
+ if the operation was successful; otherwise, .
+
+
+
+ Applies backdrop effect to the selected window handle based on the specified backdrop type.
+
+ The window handle to which the backdrop effect will be applied.
+ The type of backdrop effect to apply. This determines the visual appearance of the window's backdrop.
+ if the operation was successful; otherwise, .
+
+
+
+ Tries to remove backdrop effects if they have been applied to the .
+
+ The window from which the effect should be removed.
+
+
+
+ Tries to remove all effects if they have been applied to the hWnd.
+
+ Pointer to the window handle.
+
+
+
+ Tries to remove background from and it's composition area.
+
+ Window to manipulate.
+ if operation was successful.
+
+
+
+ No backdrop effect.
+
+
+
+
+ Sets DWMWA_SYSTEMBACKDROP_TYPE to .
+
+
+
+
+ Windows 11 Mica effect.
+
+
+
+
+ Windows Acrylic effect.
+
+
+
+
+ Windows 11 wallpaper blur effect.
+
+
+
+
+ Ways you can round windows.
+
+
+
+
+ Determined by system or application preference.
+
+
+
+
+ Do not round the corners.
+
+
+
+
+ Round the corners.
+
+
+
+
+ Round the corners slightly.
+
+
+
+
+ Converts an to an .
+
+
+
+
+ Converts a value to an .
+
+ The value to convert.
+ The type of the binding target property.
+ The converter parameter.
+ The culture to use in the converter.
+ The converted .
+
+
+
+ Converts an back to an IconSourceElement.
+
+ The value to convert.
+ The type of the binding target property.
+ The converter parameter.
+ The culture to use in the converter.
+ The converted IconSourceElement.
+
+
+
+ Helper class for Visual Studio designer.
+
+
+
+
+ Gets a value indicating whether the project is currently in design mode.
+
+
+
+
+ Gets a value indicating whether the project is currently debugged.
+
+
+
+
+ Adds an extension for that allows manipulation with HSL and HSV color spaces.
+
+
+
+
+ Maximum size with the current precision.
+
+
+
+
+ Creates a from a .
+
+ Input color.
+ Brush converted to color.
+
+
+
+ Creates a from a with defined brush opacity.
+
+ Input color.
+ Degree of opacity.
+ Brush converted to color with modified opacity.
+
+
+
+ Gets luminance based on HSL space.
+
+ Input color.
+
+
+
+ Gets brightness based on HSV space.
+
+ Input color.
+
+
+
+ Gets hue based on HSV space.
+
+ Input color.
+
+
+
+ Gets saturation based on HSV space.
+
+ Input color.
+
+
+
+ Allows to change the luminance by a factor based on the HSL color space.
+
+ Input color.
+ The value of the luminance change factor from to .
+ Updated .
+
+
+
+ Allows to change the saturation by a factor based on the HSL color space.
+
+ Input color.
+ The value of the saturation change factor from to .
+ Updated .
+
+
+
+ Allows to change the brightness by a factor based on the HSV color space.
+
+ Input color.
+ The value of the brightness change factor from to .
+ Updated .
+
+
+
+ Allows to change the brightness, saturation and luminance by a factors based on the HSL and HSV color space.
+
+ Color to convert.
+ The value of the brightness change factor from to .
+ The value of the saturation change factor from to .
+ The value of the luminance change factor from to .
+ Updated .
+
+
+
+ HSL representation models the way different paints mix together to create colour in the real world,
+ with the lightness dimension resembling the varying amounts of black or white paint in the mixture.
+
+ hue, saturation, lightness
+
+
+
+ HSV representation models how colors appear under light.
+
+ hue, saturation, brightness
+
+
+
+ Converts the color values stored as HSL to RGB.
+
+
+
+
+ Converts the color values stored as HSV (HSB) to RGB.
+
+
+
+
+ Calculates the color component for HSL.
+
+
+
+
+ Whether the floating point number is about the same.
+
+
+
+
+ Absolute percentage.
+
+
+
+
+ Absolute byte.
+
+
+
+
+ Shows the simple alert-like dialog.
+
+ Result of the life cycle of the .
+
+
+
+ Shows simple dialog
+
+ The .
+ Set of parameters of the basic dialog box.
+ The cancellation token.
+ Result of the life cycle of the .
+
+
+
+ Extensions for the .
+
+
+
+
+ Tries to apply Mica effect to the .
+
+
+
+
+ A collection of several extensions to the class.
+
+
+
+
+ Gets the number of seconds that have elapsed since the Unix epoch, excluding leap seconds. The Unix epoch is 00:00:00 UTC on 1 January 1970.
+
+
+
+
+ Gets the number of milliseconds that have elapsed since the Unix epoch, excluding leap seconds. The Unix epoch is 00:00:00 UTC on 1 January 1970.
+
+
+
+
+ Gets the number of microseconds that have elapsed since the Unix epoch, excluding leap seconds. The Unix epoch is 00:00:00 UTC on 1 January 1970.
+
+
+
+
+ Set of extensions for .
+
+
+
+
+ Gets from .
+
+ Selected frame.
+ DataContext of currently active element, otherwise .
+
+
+
+ Cleans journal.
+
+ Selected frame.
+
+
+
+ Extensions for the .
+
+
+
+
+ Sets the pane display mode of the navigation service.
+
+ The navigation service.
+ The pane display mode.
+ Same so multiple calls can be chained.
+
+
+
+ Extensions for the .
+
+
+
+
+ Shows the snackbar. If it is already visible, firstly hides it for a moment, changes its content, and then shows it again.
+
+ The .
+ Name displayed on top of snackbar.
+ Message inside the snackbar.
+
+
+
+ Shows the snackbar. If it is already visible, firstly hides it for a moment, changes its content, and then shows it again.
+
+ The .
+ Name displayed on top of snackbar.
+ Message inside the snackbar.
+ Display style.
+
+
+
+ Shows the snackbar. If it is already visible, firstly hides it for a moment, changes its content, and then shows it again.
+
+ The .
+ Name displayed on top of snackbar.
+ Message inside the snackbar.
+ Additional icon on the left.
+
+
+
+ Shows the snackbar. If it is already visible, firstly hides it for a moment, changes its content, and then shows it again.
+
+ The .
+ Name displayed on top of snackbar.
+ Message inside the snackbar.
+ The time after which the snackbar should disappear.
+
+
+
+ Shows the snackbar. If it is already visible, firstly hides it for a moment, changes its content, and then shows it again.
+
+ The .
+ Name displayed on top of snackbar.
+ Message inside the snackbar.
+ Display style.
+ The time after which the snackbar should disappear.
+
+
+
+ Shows the snackbar. If it is already visible, firstly hides it for a moment, changes its content, and then shows it again.
+
+ The .
+ Name displayed on top of snackbar.
+ Message inside the snackbar.
+ Additional icon on the left.
+ The time after which the snackbar should disappear.
+
+
+
+ Set of extensions for the enumeration of icons to facilitate their management and replacement.
+
+
+
+
+ Replaces with .
+
+
+
+
+ Replaces with .
+
+
+
+
+ Converts to based on the ID.
+
+
+
+
+ Converts to based on the ID.
+
+
+
+
+ Extension that converts the typography type enumeration to the name of the resource that represents it.
+
+
+
+
+ Converts the typography type enumeration to the name of the resource that represents it.
+
+ Name of the resource matching the . otherwise.
+
+
+
+ Extension that converts the text color type enumeration to the name of the resource that represents it.
+
+
+
+
+ Converts the text color type enumeration to the name of the resource that represents it.
+
+ Name of the resource matching the . otherwise.
+
+
+
+ Do not call it outside of NCHITTEST, NCLBUTTONUP, NCLBUTTONDOWN messages!
+
+ if mouse is over the element. otherwise.
+
+
+
+ Extensions for class.
+
+
+
+
+ Removes last segment of the .
+
+
+
+
+ Determines whether the end of is equal to provided value.
+
+
+
+
+ Append provided segments to the .
+
+
+
+
+ Append new to the .
+
+
+
+
+ Stores DPI information from which a or
+ is rendered.
+
+
+
+
+ Initializes a new instance of the structure.
+
+ The DPI scale on the X axis.
+ The DPI scale on the Y axis.
+
+
+
+ Initializes a new instance of the structure.
+
+ The DPI on the X axis.
+ The DPI on the Y axis.
+
+
+
+ Gets the DPI on the X axis.
+
+
+
+
+ Gets the DPI on the Y axis.
+
+
+
+
+ Gets the DPI scale on the X axis.
+
+
+
+
+ Gets the DPI scale on the Y axis.
+
+
+
+
+ Provides access to various DPI-related methods.
+
+
+
+
+ Default DPI value.
+
+
+
+
+ Gets DPI of the selected .
+
+ The window that you want to get information about.
+
+
+
+ Gets DPI of the selected based on it's handle.
+
+ Handle of the window that you want to get information about.
+
+
+
+ Gets the DPI values from .
+
+ The DPI values from . If the property cannot be accessed, the default value is returned.
+
+
+
+ Convert a point in device independent pixels (1/96") to a point in the system coordinates.
+
+ A point in the logical coordinate system.
+ Horizontal DPI scale.
+ Vertical DPI scale.
+ Returns the parameter converted to the system's coordinates.
+
+
+
+ Convert a point in system coordinates to a point in device independent pixels (1/96").
+
+ Returns the parameter converted to the device independent coordinate system.
+
+
+
+ Set of tools for hardware acceleration.
+
+
+
+
+ Determines whether the provided rendering tier is supported.
+
+ Hardware acceleration rendering tier to check.
+ if tier is supported.
+
+
+
+ An value whose high-order word corresponds to the rendering tier for the current thread.
+ Starting in the .NET Framework 4, rendering tier 1 has been redefined to only include graphics hardware that supports DirectX 9.0 or greater. Graphics hardware that supports DirectX 7 or 8 is now defined as rendering tier 0.
+
+
+
+
+ No graphics hardware acceleration is available for the application on the device.
+ All graphics features use software acceleration. The DirectX version level is less than version 9.0.
+
+
+
+
+ Most of the graphics features of WPF will use hardware acceleration
+ if the necessary system resources are available and have not been exhausted.
+ This corresponds to a DirectX version that is greater than or equal to 9.0.
+
+
+
+
+ Most of the graphics features of WPF will use hardware acceleration provided the
+ necessary system resources have not been exhausted.
+ This corresponds to a DirectX version that is greater than or equal to 9.0.
+
+
+
+
+ Represents a contract with the service that creates .
+
+
+
+ <ContentPresenter x:Name="RootContentDialogPresenter" Grid.Row="0" />
+
+
+ IContentDialogService contentDialogService = new ContentDialogService();
+ contentDialogService.SetContentPresenter(RootContentDialogPresenter);
+
+ await _contentDialogService.ShowAsync(
+ new ContentDialog(){
+ Title = "The cake?",
+ Content = "IS A LIE!",
+ PrimaryButtonText = "Save",
+ SecondaryButtonText = "Don't Save",
+ CloseButtonText = "Cancel"
+ }
+ );
+
+
+
+
+
+ Sets the
+
+ inside of which the dialogue will be placed. The new will replace the current .
+
+
+
+ Provides direct access to the
+
+ Reference to the currently selected which displays the 's.
+
+
+
+ Asynchronously shows the specified dialog.
+
+ The dialog to be displayed.
+ A cancellation token that can be used to cancel the operation.
+ A task that represents the asynchronous operation. The task result contains the dialog result.
+
+
+
+ Represents a contract with a that contains .
+ Through defined service allows you to use the Dependency Injection pattern in WPF UI navigation.
+
+
+
+
+ Lets you navigate to the selected page based on it's type. Should be used with .
+
+ of the page.
+ if the operation succeeds. otherwise.
+
+
+
+ Lets you navigate to the selected page based on it's type, Should be used with .
+
+ of the page.
+ DataContext
+ if the operation succeeds. otherwise.
+
+
+
+ Lets you navigate to the selected page based on it's tag. Should be used with .
+
+ Id or tag of the page.
+ if the operation succeeds. otherwise.
+
+
+
+ Lets you navigate to the selected page based on it's tag. Should be used with .
+
+ Id or tag of the page.
+ DataContext
+ if the operation succeeds. otherwise.
+
+
+
+ Synchronously adds an element to the navigation stack and navigates current navigation Frame to the
+
+ Type of control to be synchronously added to the navigation stack
+ if the operation succeeds. otherwise.
+
+
+
+ Synchronously adds an element to the navigation stack and navigates current navigation Frame to the
+
+ Type of control to be synchronously added to the navigation stack
+ DataContext
+ if the operation succeeds. otherwise.
+
+
+
+ Provides direct access to the control responsible for navigation.
+
+ Instance of the control.
+
+
+
+ Lets you attach the control that represents the .
+
+ Instance of the .
+
+
+
+ Navigates the NavigationView to the previous journal entry.
+
+ if the operation succeeds. otherwise.
+
+
+
+ Represents a contract with a that contains .
+ Through defined service allows you to use the MVVM model in WPF UI navigation.
+
+
+
+
+ Provides direct access to the control responsible for navigation.
+
+ Instance of the control.
+
+
+
+ Lets you navigate to the selected page based on it's type. Should be used with .
+
+ of the page.
+ if the operation succeeds. otherwise.
+
+
+
+ Lets you attach the service provider that delivers page instances to .
+
+ Instance of the .
+
+
+
+ Lets you attach the service that delivers page instances to .
+
+ Instance of the with attached service provider.
+
+
+
+ Triggers the command to open a window.
+
+
+
+
+ Triggers the command to close a window.
+
+
+
+
+ An interface expanding with the ability to raise
+ the event externally.
+
+
+
+
+ Notifies that the property has changed.
+
+
+
+
+ A generic interface representing a more specific version of .
+
+ The type used as argument for the interface methods.
+
+
+
+ Provides a strongly-typed variant of .
+
+ The input parameter.
+ Whether or not the current command can be executed.
+ Use this overload to avoid boxing, if is a value type.
+
+
+
+ Provides a strongly-typed variant of .
+
+ The input parameter.
+ Use this overload to avoid boxing, if is a value type.
+
+
+
+ A generic command whose sole purpose is to relay its functionality to other
+ objects by invoking delegates. The default return value for the CanExecute
+ method is . This class allows you to accept command parameters
+ in the and callback methods.
+
+ The type of parameter being passed as input to the callbacks.
+
+
+
+ The to invoke when is used.
+
+
+
+
+ The optional action to invoke when is used.
+
+
+
+
+
+
+
+ Initializes a new instance of the class that can always execute.
+
+ The execution logic.
+
+ Due to the fact that the interface exposes methods that accept a
+ nullable parameter, it is recommended that if is a reference type,
+ you should always declare it as nullable, and to always perform checks within .
+
+ Thrown if is .
+
+
+
+ Initializes a new instance of the class.
+
+ The execution logic.
+ The execution status logic.
+
+ Due to the fact that the interface exposes methods that accept a
+ nullable parameter, it is recommended that if is a reference type,
+ you should always declare it as nullable, and to always perform checks within .
+
+ Thrown if or are .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tries to get a command argument of compatible type from an input .
+
+ The input parameter.
+ The resulting value, if any.
+ Whether or not a compatible command argument could be retrieved.
+
+
+
+ Throws an if an invalid command argument is used.
+
+ The input parameter.
+ Thrown with an error message to give info on the invalid parameter.
+
+
+
+ Desktop Window Manager (DWM).
+
+
+
+
+ Cloaked flags describing why a window is cloaked.
+
+
+
+
+ GT_*
+
+
+
+
+ DWMTWR_* Tab window requirements.
+
+
+
+
+ This result means the window meets all requirements requested.
+
+
+
+
+ In some configurations, admin/user setting or mode of the system means that windows won't be tabbed
+ This requirement says that the system/mode must implement tabbing and if it does not
+ nothing can be done to change this.
+
+
+
+
+ The window has an owner or parent so is ineligible for tabbing.
+
+
+
+
+ The window has styles that make it ineligible for tabbing.
+ To be eligible windows must:
+ Have the WS_OVERLAPPEDWINDOW (WS_CAPTION, WS_THICKFRAME, etc.) styles set.
+ Not have WS_POPUP, WS_CHILD or WS_DLGFRAME set.
+ Not have WS_EX_TOPMOST or WS_EX_TOOLWINDOW set.
+
+
+
+
+ The window is ineligible due to its Dwm configuration.
+ It must not extended its client area into the title bar using DwmExtendFrameIntoClientArea
+ It must not have DWMWA_NCRENDERING_POLICY set to DWMNCRP_ENABLED
+
+
+
+
+ The window is ineligible due to it's margins, most likely due to custom handling in WM_NCCALCSIZE.
+ The window must use the default window margins for the non-client area.
+
+
+
+
+ The window has been explicitly opted out by setting DWMWA_TABBING_ENABLED to FALSE.
+
+
+
+
+ The user has configured this application to not participate in tabbing.
+
+
+
+
+ The group policy has configured this application to not participate in tabbing.
+
+
+
+
+ This is set if app compat has blocked tabs for this window. Can be overridden per window by setting
+ DWMWA_TABBING_ENABLED to TRUE. That does not override any other tabbing requirements.
+
+
+
+
+ Flags used by the DwmSetWindowAttribute function to specify the rounded corner preference for a window.
+
+
+
+
+ Backdrop types.
+
+
+
+
+ Automatically selects backdrop effect.
+
+
+
+
+ Turns off the backdrop effect.
+
+
+
+
+ Sets Mica effect with generated wallpaper tint.
+
+
+
+
+ Sets acrlic effect.
+
+
+
+
+ Sets blurred wallpaper effect, like Mica without tint.
+
+
+
+
+ Non-client rendering policy attribute values
+
+
+
+
+ Enable/disable non-client rendering based on window style
+
+
+
+
+ Disabled non-client rendering; window style is ignored
+
+
+
+
+ Enabled non-client rendering; window style is ignored
+
+
+
+
+ Sentinel value.
+
+
+
+
+ Values designating how Flip3D treats a given window.
+
+
+
+
+ Hide or include the window in Flip3D based on window style and visibility.
+
+
+
+
+ Display the window under Flip3D and disabled.
+
+
+
+
+ Display the window above Flip3D and enabled.
+
+
+
+
+ Sentinel value.
+
+
+
+
+ Options used by the DwmGetWindowAttribute and DwmSetWindowAttribute functions.
+
+
+
+
+
+ Is non-client rendering enabled/disabled
+
+
+
+
+ DWMNCRENDERINGPOLICY - Non-client rendering policy
+
+
+
+
+ Potentially enable/forcibly disable transitions
+
+
+
+
+ Enables content rendered in the non-client area to be visible on the frame drawn by DWM.
+
+
+
+
+ Retrieves the bounds of the caption button area in the window-relative space.
+
+
+
+
+ Is non-client content RTL mirrored
+
+
+
+
+ Forces the window to display an iconic thumbnail or peek representation (a static bitmap), even if a live or snapshot representation of the window is available.
+
+
+
+
+ Designates how Flip3D will treat the window.
+
+
+
+
+ Gets the extended frame bounds rectangle in screen space
+
+
+
+
+ Indicates an available bitmap when there is no better thumbnail representation.
+
+
+
+
+ Don't invoke Peek on the window.
+
+
+
+
+ LivePreview exclusion information
+
+
+
+
+ Cloaks the window such that it is not visible to the user.
+
+
+
+
+ If the window is cloaked, provides one of the following values explaining why.
+
+
+
+
+ Freeze the window's thumbnail image with its current visuals. Do no further live updates on the thumbnail image to match the window's contents.
+
+
+
+
+ BOOL, Updates the window only when desktop composition runs for other reasons
+
+
+
+
+ BOOL, Allows the use of host backdrop brushes for the window.
+
+
+
+
+ Allows a window to either use the accent color, or dark, according to the user Color Mode preferences.
+
+
+
+
+ Allows a window to either use the accent color, or dark, according to the user Color Mode preferences.
+
+
+
+
+ Controls the policy that rounds top-level window corners.
+ Windows 11 and above.
+
+
+
+
+ The color of the thin border around a top-level window.
+
+
+
+
+ The color of the caption.
+ Windows 11 and above.
+
+
+
+
+ The color of the caption text.
+ Windows 11 and above.
+
+
+
+
+ Width of the visible border around a thick frame window.
+ Windows 11 and above.
+
+
+
+
+ Allows to enter a value from 0 to 4 deciding on the imposed backdrop effect.
+
+
+
+
+ Indicates whether the window should use the Mica effect.
+ Windows 11 and above.
+
+
+
+
+ Represents the current DWM color accent settings.
+
+
+
+
+ ColorizationColor
+
+
+
+
+ ColorizationAfterglow.
+
+
+
+
+ ColorizationColorBalance.
+
+
+
+
+ ColorizationAfterglowBalance.
+
+
+
+
+ ColorizationBlurBalance.
+
+
+
+
+ ColorizationGlassReflectionIntensity.
+
+
+
+
+ ColorizationOpaqueBlend.
+
+
+
+
+ Defines a data type used by the Desktop Window Manager (DWM) APIs. It represents a generic ratio and is used for different purposes and units even within a single API.
+
+
+
+
+ The ratio numerator.
+
+
+
+
+ The ratio denominator.
+
+
+
+
+ Specifies the input operations for which visual feedback should be provided. This enumeration is used by the DwmShowContact function.
+
+
+
+
+ Flags used by the DwmSetPresentParameters function to specify the frame sampling type.
+
+
+
+
+ Use the first source frame that includes the first refresh of the output frame
+
+
+
+
+ Use the source frame that includes the most refreshes of out the output frame
+ in case of multiple source frames with the same coverage the last will be used
+
+
+
+
+ Sentinel value.
+
+
+
+
+ Specifies Desktop Window Manager (DWM) composition timing information. Used by the function.
+
+
+
+
+ SIT flags.
+
+
+
+
+ None.
+
+
+
+
+ Displays a frame around the provided bitmap.
+
+
+
+
+ Obtains a value that indicates whether Desktop Window Manager (DWM) composition is enabled.
+
+ A pointer to a value that, when this function returns successfully, receives TRUE if DWM composition is enabled; otherwise, FALSE.
+ If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
+
+
+
+ Extends the window frame into the client area.
+
+ The handle to the window in which the frame will be extended into the client area.
+ A pointer to a MARGINS structure that describes the margins to use when extending the frame into the client area.
+
+
+
+ Retrieves the current composition timing information for a specified window.
+
+ The handle to the window for which the composition timing information should be retrieved.
+ A pointer to a structure that, when this function returns successfully, receives the current composition timing information for the window.
+
+
+
+ Called by an application to indicate that all previously provided iconic bitmaps from a window, both thumbnails and peek representations, should be refreshed.
+
+ A handle to the window or tab whose bitmaps are being invalidated through this call. This window must belong to the calling process.
+
+
+
+ Sets a static, iconic bitmap on a window or tab to use as a thumbnail representation. The taskbar can use this bitmap as a thumbnail switch target for the window or tab.
+
+ A handle to the window or tab. This window must belong to the calling process.
+ A handle to the bitmap to represent the window that hwnd specifies.
+ The display options for the thumbnail.
+
+
+
+ Sets a static, iconic bitmap to display a live preview (also known as a Peek preview) of a window or tab. The taskbar can use this bitmap to show a full-sized preview of a window or tab.
+
+ A handle to the window. This window must belong to the calling process.
+ A handle to the bitmap to represent the window that hwnd specifies.
+ The offset of a tab window's client region (the content area inside the client window frame) from the host window's frame. This offset enables the tab window's contents to be drawn correctly in a live preview when it is drawn without its frame.
+ The display options for the live preview.
+
+
+
+ Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window.
+
+ The handle to the window for which the attribute value is to be set.
+ A flag describing which value to set, specified as a value of the DWMWINDOWATTRIBUTE enumeration.
+ A pointer to an object containing the attribute value to set.
+ The size, in bytes, of the attribute value being set via the pvAttribute parameter.
+ If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
+
+
+
+ Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window.
+
+ The handle to the window for which the attribute value is to be set.
+ A flag describing which value to set, specified as a value of the DWMWINDOWATTRIBUTE enumeration.
+ A pointer to an object containing the attribute value to set.
+ The size, in bytes, of the attribute value being set via the pvAttribute parameter.
+ If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
+
+
+
+ Sets the value of Desktop Window Manager (DWM) non-client rendering attributes for a window.
+
+ The handle to the window for which the attribute value is to be set.
+ A flag describing which value to set, specified as a value of the DWMWINDOWATTRIBUTE enumeration.
+ A pointer to an object containing the attribute value to set.
+ The size, in bytes, of the attribute value being set via the pvAttribute parameter.
+ If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
+
+
+
+ Retrieves the current value of a specified Desktop Window Manager (DWM) attribute applied to a window. For programming guidance, and code examples, see Controlling non-client region rendering.
+
+ The handle to the window from which the attribute value is to be retrieved.
+ A flag describing which value to retrieve, specified as a value of the enumeration.
+ A pointer to a value which, when this function returns successfully, receives the current value of the attribute. The type of the retrieved value depends on the value of the dwAttribute parameter.
+ The size, in bytes, of the attribute value being received via the pvAttribute parameter.
+ If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
+
+
+
+ Retrieves the current value of a specified Desktop Window Manager (DWM) attribute applied to a window. For programming guidance, and code examples, see Controlling non-client region rendering.
+
+ The handle to the window from which the attribute value is to be retrieved.
+ A flag describing which value to retrieve, specified as a value of the enumeration.
+ A pointer to a value which, when this function returns successfully, receives the current value of the attribute. The type of the retrieved value depends on the value of the dwAttribute parameter.
+ The size, in bytes, of the attribute value being received via the pvAttribute parameter.
+ If the function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
+
+
+
+ The feature is not included in the Microsoft documentation. Reads Desktop Window Manager (DWM) color information.
+
+ A pointer to a reference value that will hold the color information.
+
+
+
+ Common Windows API result;
+
+
+
+
+ Operation successful.
+
+
+
+
+ Operation successful.
+
+
+
+
+ Operation successful.
+
+
+
+
+ Unspecified failure.
+
+
+
+
+ Used by multiple technologies.
+
+
+
+
+ Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. Multiple threads do not overwrite each other's last-error code.
+
+ The return value is the calling thread's last-error code.
+
+
+
+ Sets the last-error code for the calling thread.
+
+ The last-error code for the thread.
+
+
+
+ Determines whether the calling process is being debugged by a user-mode debugger.
+
+ If the current process is running in the context of a debugger, the return value is nonzero.
+
+
+
+ Windows kernel module.
+
+
+
+
+ The Windows UI provides users with access to a wide variety of objects necessary to run applications and manage the operating system.
+
+
+
+
+ DATAOBJ_GET_ITEM_FLAGS. DOGIF_*.
+
+
+
+
+ Shell_NotifyIcon messages. NIM_*
+
+
+
+
+ Shell_NotifyIcon flags. NIF_*
+
+
+
+
+ Vista only.
+
+
+
+
+ Vista only.
+
+
+
+
+ The size of this structure, in bytes.
+
+
+
+
+ A handle to the window that receives notifications associated with an icon in the notification area.
+
+
+
+
+ The application-defined identifier of the taskbar icon. The Shell uses either (hWnd plus uID) or guidItem to identify which icon to operate on when Shell_NotifyIcon is invoked.
+ You can have multiple icons associated with a single hWnd by assigning each a different uID. If guidItem is specified, uID is ignored.
+
+
+
+
+ Flags that either indicate which of the other members of the structure contain valid data or provide additional information to the tooltip as to how it should display.
+
+
+
+
+ 0x00000001. The uCallbackMessage member is valid.
+
+
+
+
+ 0x00000002. The hIcon member is valid.
+
+
+
+
+ 0x00000004. The szTip member is valid.
+
+
+
+
+ The state of the icon. There are two flags that can be set independently.
+ NIS_HIDDEN = 1. The icon is hidden.
+ NIS_SHAREDICON = 2. The icon is shared.
+
+
+
+
+ Prior to Vista this was a union of uTimeout and uVersion. As of Vista, uTimeout has been deprecated.
+
+
+
+
+ Sets the User Model AppID for the current process, enabling Windows to retrieve this ID
+
+ The string ID
+
+
+
+ Retrieves the User Model AppID that has been explicitly set for the current process via SetCurrentProcessExplicitAppUserModelID
+
+ The returned string ID of the Application User Model.
+ An HRESULT indicating success or failure of the operation.
+
+
+
+ Exposes methods that enumerate the contents of a view and receive notification from callback upon enumeration completion.
+
+
+
+
+ THUMBBUTTON flags. THBF_*
+
+
+
+
+ THUMBBUTTON mask. THB_*
+
+
+
+
+ TBPF_*
+
+
+
+
+ STPF_*
+
+
+
+
+ EBO_*
+
+
+
+
+ EBF_*
+
+
+
+
+ THUMBBUTTON
+
+
+
+
+ WPARAM value for a THUMBBUTTON being clicked.
+
+
+
+
+ Class DECLSPEC_UUID("56FDF344-FD6D-11d0-958A-006097C9A090")
+
+
+
+
+ Class DECLSPEC_UUID("9ac9fbe1-e0a2-4ad6-b4ee-e212013ea917")
+
+
+
+
+ MIDL_INTERFACE("c43dc798-95d1-4bea-9030-bb99e2983a1a")
+ ITaskbarList4 : public ITaskbarList3
+
+
+
+
+ Adds thumbnail toolbar buttons to the specified window.
+
+ Window handle.
+ Number of buttons.
+ Array of buttons.
+ HRESULT
+
+
+
+ Updates thumbnail toolbar buttons for the specified window.
+
+ Window handle.
+ Number of buttons.
+ Array of buttons to update.
+ HRESULT
+
+
+
+ A set of dangerous methods to modify the appearance.
+
+
+
+
+ Tries to set the corner preference.
+
+ Selected window.
+ Window corner preference.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to set the corner preference of the selected window.
+
+ Selected window handle.
+ Window corner preference.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to remove ImmersiveDarkMode effect from the .
+
+ The window to which the effect is to be applied.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to remove ImmersiveDarkMode effect from the window handle.
+
+ Window handle.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to apply ImmersiveDarkMode effect for the .
+
+ The window to which the effect is to be applied.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to apply ImmersiveDarkMode effect for the window handle.
+
+ Window handle.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to remove titlebar from selected .
+
+ The window to which the effect is to be applied.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to remove titlebar from selected window handle.
+
+ Window handle.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to apply selected backdrop type for window handle.
+
+ Selected window handle.
+ Backdrop type.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to determine whether the provided has applied legacy backdrop effect.
+
+ Window handle.
+ Background backdrop type.
+
+
+
+ Tries to determine whether the provided has applied legacy Mica effect.
+
+ Window to check.
+
+
+
+ Tries to determine whether the provided handle has applied legacy Mica effect.
+
+ Window handle.
+
+
+
+ Tries to apply legacy Mica effect for the selected .
+
+ The window to which the effect is to be applied.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to apply legacy Mica effect for the selected .
+
+ Window handle.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to apply legacy Acrylic effect for the selected .
+
+ The window to which the effect is to be applied.
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to apply legacy Acrylic effect for the selected .
+
+ Window handle
+ if invocation of native Windows function succeeds.
+
+
+
+ Tries to get currently selected Window accent color.
+
+
+
+
+ Tries to set taskbar state for the selected window handle.
+
+ Window handle.
+ Taskbar flag.
+
+
+
+ Updates the taskbar progress bar value for a window.
+
+ The handle to the window.
+ Progress state flag (paused, etc).
+ Current progress value.
+ Maximum progress value.
+ True if successful updated, otherwise false.
+
+
+
+ Checks whether the DWM composition is enabled.
+
+
+
+
+ Checks if provided pointer represents existing window.
+
+
+
+
+ Tries to get the pointer to the window handle.
+
+ if the handle is not .
+
+
+
+ A set of dangerous methods to modify the appearance.
+
+
+
+
+ Casts to .
+
+
+
+
+ Casts to .
+
+
+
+
+ Casts to .
+
+
+
+
+ Casts to .
+
+
+
+
+ USER procedure declarations, constant definitions and macros.
+
+
+
+
+ SetWindowPos options
+
+
+
+
+ EnableMenuItem uEnable values, MF_*
+
+
+
+
+ Possible return value for EnableMenuItem
+
+
+
+
+ Menu item element.
+
+
+
+
+ SCF_ISSECURE
+
+
+
+
+ WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
+
+
+
+
+ Hit test returned error.
+
+
+
+
+ Hit test returned transparent.
+
+
+
+
+ On the screen background or on a dividing line between windows.
+
+
+
+
+ In a client area.
+
+
+
+
+ In a title bar.
+
+
+
+
+ In a window menu or in a Close button in a child window.
+
+
+
+
+ In a size box (same as HTSIZE).
+
+
+
+
+ In a menu.
+
+
+
+
+ In a horizontal scroll bar.
+
+
+
+
+ In the vertical scroll bar.
+
+
+
+
+ In a Minimize button.
+
+
+
+
+ In a Maximize button.
+
+
+
+
+ In the left border of a resizable window (the user can click the mouse to resize the window horizontally).
+
+
+
+
+ In the right border of a resizable window (the user can click the mouse to resize the window horizontally).
+
+
+
+
+ In the upper-horizontal border of a window.
+
+
+
+
+ Window long flags.
+
+
+
+
+
+ Sets a new extended window style.
+
+
+
+
+ Sets a new application instance handle.
+
+
+
+
+ Sets a new hWnd parent.
+
+
+
+
+ Sets a new identifier of the child window. The window cannot be a top-level window.
+
+
+
+
+ Sets a new window style.
+
+
+
+
+ Sets the user data associated with the window.
+ This data is intended for use by the application that created the window. Its value is initially zero.
+
+
+
+
+ Sets a new address for the window procedure.
+ You cannot change this attribute if the window does not belong to the same process as the calling thread.
+
+
+
+
+ Sets new extra information that is private to the application, such as handles or pointers.
+
+
+
+
+ Sets the return value of a message processed in the dialog box procedure.
+
+
+
+
+ Sets the new address of the dialog box procedure.
+
+
+
+
+ Window composition attributes.
+
+
+
+
+ DWM window accent state.
+
+
+
+
+ WCA window accent policy.
+
+
+
+
+ CS_*
+
+
+
+
+ MSGFLT_*. New in Vista. Realiased in Windows 7.
+
+
+
+
+ Resets the window message filter for hWnd to the default. Any message allowed globally or process-wide will get through, but any message not included in those two categories, and which comes from a lower privileged process, will be blocked.
+
+
+
+
+ Allows the message through the filter. This enables the message to be received by hWnd, regardless of the source of the message, even it comes from a lower privileged process.
+
+
+
+
+ Blocks the message to be delivered to hWnd if it comes from a lower privileged process, unless the message is allowed process-wide by using the ChangeWindowMessageFilter function or globally.
+
+
+
+
+ MSGFLTINFO.
+
+
+
+
+ Win7 only.
+
+
+
+
+ Window message values, WM_*
+
+
+
+
+ This is the hard-coded message value used by WinForms for Shell_NotifyIcon.
+ It's relatively safe to reuse.
+
+
+
+
+ WindowStyle values, WS_*
+
+
+
+
+ Window style extended values, WS_EX_*
+
+
+
+
+ SystemMetrics. SM_*
+
+
+
+
+ ShowWindow options
+
+
+
+
+ Contains window class information. It is used with the and GetClassInfoEx functions.
+
+
+
+
+ The size, in bytes, of this structure. Set this member to sizeof(WNDCLASSEX). Be sure to set this member before calling the GetClassInfoEx function.
+
+
+
+
+ The class style(s). This member can be any combination of the Class Styles.
+
+
+
+
+ A pointer to the window procedure. You must use the CallWindowProc function to call the window procedure. For more information, see WindowProc.
+
+
+
+
+ The number of extra bytes to allocate following the window-class structure. The system initializes the bytes to zero.
+
+
+
+
+ The number of extra bytes to allocate following the window instance. The system initializes the bytes to zero. If an application uses WNDCLASSEX to register a dialog box created by using the CLASS directive in the resource file, it must set this member to DLGWINDOWEXTRA.
+
+
+
+
+ A handle to the instance that contains the window procedure for the class.
+
+
+
+
+ A handle to the class icon. This member must be a handle to an icon resource. If this member is NULL, the system provides a default icon.
+
+
+
+
+ A handle to the class cursor. This member must be a handle to a cursor resource. If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window.
+
+
+
+
+ A handle to the class background brush. This member can be a handle to the brush to be used for painting the background, or it can be a color value.
+
+
+
+
+ Pointer to a null-terminated character string that specifies the resource name of the class menu, as the name appears in the resource file. If you use an integer to identify the menu, use the MAKEINTRESOURCE macro. If this member is NULL, windows belonging to this class have no default menu.
+
+
+
+
+ A pointer to a null-terminated string or is an atom. If this parameter is an atom, it must be a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpszClassName; the high-order word must be zero.
+
+
+
+
+ A handle to a small icon that is associated with the window class. If this member is NULL, the system searches the icon resource specified by the hIcon member for an icon of the appropriate size to use as the small icon.
+
+
+
+
+ Delegate declaration that matches native WndProc signatures.
+
+
+
+
+ Delegate declaration that matches native WndProc signatures.
+
+
+
+
+ Delegate declaration that matches managed WndProc signatures.
+
+
+
+
+ The ReleaseDC function releases a device context (DC), freeing it for use by other applications.
+ The effect of the ReleaseDC function depends on the type of DC. It frees only common and window DCs. It has no effect on class or private DCs.
+
+ A handle to the window whose DC is to be released.
+ A handle to the DC to be released.
+ The return value indicates whether the DC was released. If the DC was released, the return value is 1. If the DC was not released, the return value is zero.
+
+
+
+ Calculates the required size of the window rectangle, based on the desired size of the client rectangle.
+ The window rectangle can then be passed to the CreateWindowEx function to create a window whose client area is the desired size.
+
+ A pointer to a RECT structure that contains the coordinates of the top-left and bottom-right corners of the desired client area.
+ The window style of the window whose required size is to be calculated. Note that you cannot specify the WS_OVERLAPPED style.
+ Indicates whether the window has a menu.
+ The extended window style of the window whose required size is to be calculated.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ [Using the ChangeWindowMessageFilter function is not recommended, as it has process-wide scope. Instead, use the ChangeWindowMessageFilterEx function to control access to specific windows as needed. ChangeWindowMessageFilter may not be supported in future versions of Windows.
+ Adds or removes a message from the User Interface Privilege Isolation(UIPI) message filter.
+
+ The message to add to or remove from the filter.
+ The action to be performed. One of the following values.
+ if successful; otherwise, . To get extended error information, call .
+
+
+
+ Modifies the User Interface Privilege Isolation (UIPI) message filter for a specified window.
+
+ A handle to the window whose UIPI message filter is to be modified.
+ The message that the message filter allows through or blocks.
+ The action to be performed.
+ Optional pointer to a structure.
+ If the function succeeds, it returns ; otherwise, it returns . To get extended error information, call .
+
+
+
+ Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
+ Unicode declaration for
+
+ A handle to the window whose window procedure is to receive the message.
+ The message to be posted.
+ Additional message-specific information.
+ Additional message-specific information.~
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
+ ANSI declaration for
+
+ A handle to the window whose window procedure is to receive the message.
+ The message to be posted.
+ Additional message-specific information.
+ Additional message-specific information.~
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message.
+
+ A handle to the window whose window procedure is to receive the message.
+ The message to be posted.
+ Additional message-specific information.
+ Additional message-specific information.~
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Sends the specified message to a window or windows. The SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.
+
+ A handle to the window whose window procedure will receive the message.
+ The message to be sent.
+ Additional message-specific information.
+ Additional message-specific information.~
+ The return value specifies the result of the message processing; it depends on the message sent.
+
+
+
+ Creates an overlapped, pop-up, or child window with an extended window style; otherwise,
+ this function is identical to the CreateWindow function. For more information about
+ creating a window and for full descriptions of the other parameters of CreateWindowEx, see CreateWindow.
+
+ The extended window style of the window being created.
+ A null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function.
+ The window name. If the window style specifies a title bar, the window title pointed to by lpWindowName is displayed in the title bar.
+ The style of the window being created. This parameter can be a combination of the window style values, plus the control styles indicated in the Remarks section.
+ The initial horizontal position of the window. For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, in screen coordinates.
+ The initial vertical position of the window. For an overlapped or pop-up window, the y parameter is the initial y-coordinate of the window's upper-left corner, in screen coordinates.
+ The width, in device units, of the window. For overlapped windows, nWidth is the window's width, in screen coordinates, or CW_USEDEFAULT.
+ The height, in device units, of the window. For overlapped windows, nHeight is the window's height, in screen coordinates. If the nWidth parameter is set to CW_USEDEFAULT, the system ignores nHeight.
+ A handle to the parent or owner window of the window being created. To create a child window or an owned window, supply a valid window handle. This parameter is optional for pop-up windows.
+ A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used.
+ A handle to the instance of the module to be associated with the window.
+ Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns.
+ If the function succeeds, the return value is a handle to the new window.
+
+
+
+ Creates an overlapped, pop-up, or child window with an extended window style; otherwise,
+ this function is identical to the CreateWindow function. For more information about
+ creating a window and for full descriptions of the other parameters of CreateWindowEx, see CreateWindow.
+
+ The extended window style of the window being created.
+ A null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function.
+ The window name. If the window style specifies a title bar, the window title pointed to by lpWindowName is displayed in the title bar.
+ The style of the window being created. This parameter can be a combination of the window style values, plus the control styles indicated in the Remarks section.
+ The initial horizontal position of the window. For an overlapped or pop-up window, the x parameter is the initial x-coordinate of the window's upper-left corner, in screen coordinates.
+ The initial vertical position of the window. For an overlapped or pop-up window, the y parameter is the initial y-coordinate of the window's upper-left corner, in screen coordinates.
+ The width, in device units, of the window. For overlapped windows, nWidth is the window's width, in screen coordinates, or CW_USEDEFAULT.
+ The height, in device units, of the window. For overlapped windows, nHeight is the window's height, in screen coordinates. If the nWidth parameter is set to CW_USEDEFAULT, the system ignores nHeight.
+ A handle to the parent or owner window of the window being created. To create a child window or an owned window, supply a valid window handle. This parameter is optional for pop-up windows.
+ A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used.
+ A handle to the instance of the module to be associated with the window.
+ Pointer to a value to be passed to the window through the CREATESTRUCT structure (lpCreateParams member) pointed to by the lParam param of the WM_CREATE message. This message is sent to the created window by this function before it returns.
+ If the function succeeds, the return value is a handle to the new window.
+
+
+
+ Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
+ Unicode declaration for
+
+ A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function.
+ If the function succeeds, the return value is a class atom that uniquely identifies the class being registered.
+
+
+
+ Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
+ ANSI declaration for
+
+ A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function.
+ If the function succeeds, the return value is a class atom that uniquely identifies the class being registered.
+
+
+
+ Registers a window class for subsequent use in calls to the CreateWindow or CreateWindowEx function.
+
+ A pointer to a structure. You must fill the structure with the appropriate class attributes before passing it to the function.
+ If the function succeeds, the return value is a class atom that uniquely identifies the class being registered.
+
+
+
+ Calls the default window procedure to provide default processing for any window messages that an application does not process.
+ This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure.
+ Unicode declaration for
+
+ A handle to the window procedure that received the message.
+ The message.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.~
+ The return value is the result of the message processing and depends on the message.
+
+
+
+ Calls the default window procedure to provide default processing for any window messages that an application does not process.
+ This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure.
+ ANSI declaration for
+
+ A handle to the window procedure that received the message.
+ The message.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.~
+ The return value is the result of the message processing and depends on the message.
+
+
+
+ Calls the default window procedure to provide default processing for any window messages that an application does not process.
+ This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure.
+
+ A handle to the window procedure that received the message.
+ The message.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.
+ Additional message information. The content of this parameter depends on the value of the Msg parameter.~
+ The return value is the result of the message processing and depends on the message.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the 32-bit (DWORD) value at the specified offset into the extra window memory.
+ If you are retrieving a pointer or a handle, this function has been superseded by the function.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Retrieves information about the specified window. The function also retrieves the value at a specified offset into the extra window memory.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be retrieved.
+ If the function succeeds, the return value is the requested value.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
+ Note: This function has been superseded by the function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window memory, minus the size of an integer.
+ New window style.
+ If the function succeeds, the return value is the previous value of the specified 32-bit integer.
+
+
+
+ Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.
+ Unicode declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified offset.
+
+
+
+ Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.
+ ANSI declaration for
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified offset.
+
+
+
+ Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory.
+
+ A handle to the window and, indirectly, the class to which the window belongs.
+ The zero-based offset to the value to be set.
+ The replacement value.
+ If the function succeeds, the return value is the previous value of the specified offset.
+
+
+
+ Changes an attribute of the specified window.
+
+
+
+
+ Destroys an icon and frees any memory the icon occupied.
+
+ A handle to the icon to be destroyed. The icon must not be in use.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Determines whether the specified window handle identifies an existing window.
+
+ A handle to the window to be tested.
+ If the window handle identifies an existing window, the return value is nonzero.
+
+
+
+ Destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus from it.
+
+ A handle to the window to be destroyed.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Retrieves the show state and the restored, minimized, and maximized positions of the specified window.
+
+ A handle to the window.
+ A pointer to the structure that receives the show state and position information. Before calling GetWindowPlacement, set the length member to sizeof(WINDOWPLACEMENT). GetWindowPlacement fails if lpwndpl-> length is not set correctly.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that are relative to the upper-left corner of the screen.
+
+ A handle to the window.
+ A pointer to a RECT structure that receives the screen coordinates of the upper-left and lower-right corners of the window.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Determines the visibility state of the specified window.
+
+ A handle to the window to be tested.
+ If the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero.
+
+
+
+ Determines whether the specified window is enabled for mouse and keyboard input.
+
+ A handle to the window to be tested.
+ If the window is enabled, the return value is nonzero.
+
+
+
+ The MonitorFromWindow function retrieves a handle to the display monitor that has the largest area of intersection with the bounding rectangle of a specified window.
+
+ A handle to the window of interest.
+ Determines the function's return value if the window does not intersect any display monitor.
+ If the window intersects one or more display monitor rectangles, the return value is an HMONITOR handle to the display monitor that has the largest area of intersection with the window.
+
+
+
+ Retrieves the specified system metric or system configuration setting.
+ Note that all dimensions retrieved by GetSystemMetrics are in pixels.
+
+ The system metric or configuration setting to be retrieved. This parameter can be one of the values.
+ Note that all SM_CX* values are widths and all SM_CY* values are heights. Also note that all settings designed to return Boolean data represent as any nonzero value, and as a zero value.
+ If the function succeeds, the return value is the requested system metric or configuration setting.
+
+
+
+ Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages.
+ Unicode declaration for
+
+ The message to be registered.
+ If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.
+
+
+
+ Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages.
+ ANSI declaration for
+
+ The message to be registered.
+ If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.
+
+
+
+ Defines a new window message that is guaranteed to be unique throughout the system. The message value can be used when sending or posting messages.
+
+ The message to be registered.
+ If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.
+
+
+
+ Activates a window. The window must be attached to the calling thread's message queue.
+
+ A handle to the top-level window to be activated.
+ If the function succeeds, the return value is the handle to the window that was previously active.
+
+
+
+ Brings the thread that created the specified window into the foreground and activates the window.
+ Keyboard input is directed to the window, and various visual cues are changed for the user.
+ The system assigns a slightly higher priority to the thread that created the foreground window than it does to other threads.
+
+ A handle to the window that should be activated and brought to the foreground.
+ If the window was brought to the foreground, the return value is nonzero.
+
+
+
+ Retrieves the position of the mouse cursor, in screen coordinates.
+
+ A pointer to a structure that receives the screen coordinates of the cursor.
+ Returns nonzero if successful or zero otherwise. To get extended error information, call .
+
+
+
+ Enables, disables, or grays the specified menu item.
+
+ A handle to the menu.
+ The menu item to be enabled, disabled, or grayed, as determined by the uEnable parameter.
+ Controls the interpretation of the uIDEnableItem parameter and indicate whether the menu item is enabled, disabled, or grayed.
+ The return value specifies the previous state of the menu item (it is either MF_DISABLED, MF_ENABLED, or MF_GRAYED). If the menu item does not exist, the return value is -1 ().
+
+
+
+ The SetWindowRgn function sets the window region of a window. The window region determines the area within the window where the system permits drawing. The system does not display any portion of a window that lies outside of the window region.
+
+ A handle to the window whose window region is to be set.
+ A handle to a region. The function sets the window region of the window to this region.
+ Specifies whether the system redraws the window after setting the window region. If bRedraw is , the system does so; otherwise, it does not.
+ Native method returned HRESULT.
+
+
+
+ Changes the size, position, and Z order of a child, pop-up, or top-level window. These windows are ordered according to their appearance on the screen. The topmost window receives the highest rank and is the first window in the Z order.
+
+ A handle to the window.
+ A handle to the window to precede the positioned window in the Z order.
+ The new position of the left side of the window, in client coordinates.
+ The new position of the top of the window, in client coordinates.
+ The new width of the window, in pixels.
+ The new height of the window, in pixels.
+ The window sizing and positioning flags.
+ If the function succeeds, the return value is nonzero.
+
+
+
+ Sets the process-default DPI awareness to system-DPI awareness. This is equivalent to calling SetProcessDpiAwarenessContext with a DPI_AWARENESS_CONTEXT value of DPI_AWARENESS_CONTEXT_SYSTEM_AWARE.
+
+
+
+
+ Sets various information regarding DWM window attributes.
+
+
+
+
+ Sets various information regarding DWM window attributes.
+
+
+
+
+ Returns the dots per inch (dpi) value for the specified window.
+
+ The window that you want to get information about.
+ The DPI for the window, which depends on the DPI_AWARENESS of the window.
+
+
+
+ Returns the dots per inch (dpi) value for the specified window.
+
+ The window that you want to get information about.
+ The DPI for the window, which depends on the DPI_AWARENESS of the window.
+
+
+
+ Returned by the GetThemeMargins function to define the margins of windows that have visual styles applied.
+
+
+
+
+ Width of left border that retains its size.
+
+
+
+
+ Width of right border that retains its size.
+
+
+
+
+ Height of top border that retains its size.
+
+
+
+
+ Height of bottom border that retains its size.
+
+
+
+
+ Specifies the type of visual style attribute to set on a window.
+
+
+
+
+ Non-client area window attributes will be set.
+
+
+
+
+ WindowThemeNonClientAttributes
+
+
+
+
+ Prevents the window caption from being drawn.
+
+
+
+
+ Prevents the system icon from being drawn.
+
+
+
+
+ Prevents the system icon menu from appearing.
+
+
+
+
+ Prevents mirroring of the question mark, even in right-to-left (RTL) layout.
+
+
+
+
+ A mask that contains all the valid bits.
+
+
+
+
+ Defines options that are used to set window visual style attributes.
+
+
+
+
+ A combination of flags that modify window visual style attributes.
+ Can be a combination of the WTNCA constants.
+
+
+
+
+ A bitmask that describes how the values specified in dwFlags should be applied.
+ If the bit corresponding to a value in dwFlags is 0, that flag will be removed.
+ If the bit is 1, the flag will be added.
+
+
+
+
+ Sets attributes to control how visual styles are applied to a specified window.
+
+
+ Handle to a window to apply changes to.
+
+
+ Value of type WINDOWTHEMEATTRIBUTETYPE that specifies the type of attribute to set.
+ The value of this parameter determines the type of data that should be passed in the pvAttribute parameter.
+ Can be the following value:
+ WTA_NONCLIENT (Specifies non-client related attributes).
+ pvAttribute must be a pointer of type WTA_OPTIONS.
+
+
+ A pointer that specifies attributes to set. Type is determined by the value of the eAttribute value.
+
+
+ Specifies the size, in bytes, of the data pointed to by pvAttribute.
+
+
+
+
+ Tests if a visual style for the current application is active.
+
+ if a visual style is enabled, and windows with visual styles applied should call OpenThemeData to start using theme drawing services.
+
+
+
+ Retrieves the name of the current visual style, and optionally retrieves the color scheme name and size name.
+
+ Pointer to a string that receives the theme path and file name.
+ Value of type int that contains the maximum number of characters allowed in the theme file name.
+ Pointer to a string that receives the color scheme name. This parameter may be set to NULL.
+ Value of type int that contains the maximum number of characters allowed in the color scheme name.
+ Pointer to a string that receives the size name. This parameter may be set to NULL.
+ Value of type int that contains the maximum number of characters allowed in the size name.
+ HRESULT
+
+
+
+ The POINT structure defines the x- and y-coordinates of a point.
+
+
+
+
+ Specifies the x-coordinate of the point.
+
+
+
+
+ Specifies the y-coordinate of the point.
+
+
+
+
+ The structure defines the x- and y-coordinates of a point.
+
+
+
+
+ Specifies the x-coordinate of the point.
+
+
+
+
+ Specifies the y-coordinate of the point.
+
+
+
+
+ The RECT structure defines a rectangle by the coordinates of its upper-left and lower-right corners.
+
+
+
+
+ Gets or sets the x-coordinate of the upper-left corner of the rectangle.
+
+
+
+
+ Gets or sets the x-coordinate of the lower-right corner of the rectangle.
+
+
+
+
+ Gets or sets the y-coordinate of the upper-left corner of the rectangle.
+
+
+
+
+ Gets or sets the y-coordinate of the lower-right corner of the rectangle.
+
+
+
+
+ Gets the width of the rectangle.
+
+
+
+
+ Gets the height of the rectangle.
+
+
+
+
+ Gets the position of the rectangle.
+
+
+
+
+ Gets the size of the rectangle.
+
+
+
+
+ Sets offset of the rectangle.
+
+
+
+
+ Combines two RECTs.
+
+
+
+
+
+
+
+
+
+
+ The RECTL structure defines a rectangle by the coordinates of its upper-left and lower-right corners.
+
+
+
+
+ Gets or sets the x-coordinate of the upper-left corner of the rectangle.
+
+
+
+
+ Gets or sets the x-coordinate of the lower-right corner of the rectangle.
+
+
+
+
+ Gets or sets the y-coordinate of the upper-left corner of the rectangle.
+
+
+
+
+ Gets or sets the y-coordinate of the lower-right corner of the rectangle.
+
+
+
+
+ Gets the width of the rectangle.
+
+
+
+
+ Gets the height of the rectangle.
+
+
+
+
+ Gets the position of the rectangle.
+
+
+
+
+ Gets the size of the rectangle.
+
+
+
+
+ Sets offset of the rectangle.
+
+
+
+
+ Combines two RECTLs
+
+
+
+
+
+
+
+
+
+
+ The SIZE structure defines the width and height of a rectangle.
+
+
+
+
+ Specifies the rectangle's width. The units depend on which function uses this structure.
+
+
+
+
+ Specifies the rectangle's height. The units depend on which function uses this structure.
+
+
+
+
+ Represents a contract with the service that provides global .
+
+
+
+
+ Gets or sets a time for which the should be visible. (By default 2 seconds)
+
+
+
+
+ Sets the
+
+ inside of which the snackbar will be placed. The new will replace the current .
+
+
+
+ Provides direct access to the
+
+ currently in use.
+
+
+
+ Shows the snackbar. If it is already visible, firstly hides it for a moment, changes its content, and then shows it again.
+
+ Name displayed on top of snackbar.
+ Message inside the snackbar.
+ Display style.
+ Additional icon on the left.
+ The time after which the snackbar should disappear.
+
+
+
+ Represents a contract with a service that provides methods for manipulating the taskbar.
+
+
+
+
+ Gets taskbar state of the selected window handle.
+
+ Window handle.
+ The current state of system TaskBar.
+
+
+
+ Gets taskbar state of the selected window.
+
+ Selected window.
+ The current state of system TaskBar.
+
+
+
+ Sets taskbar state of the selected window handle.
+
+ Window handle to modify.
+ Progress sate to set.
+ if the operation succeeds. otherwise.
+
+
+
+ Sets taskbar value of the selected window handle.
+
+ Window handle to modify.
+ Progress sate to set.
+ Current value to display.
+ Maximum number for division.
+ if the operation succeeds. otherwise.
+
+
+
+ Sets taskbar value of the selected window handle.
+
+ Window handle to modify.
+ Current value to display.
+ Maximum number for division.
+ if the operation succeeds. otherwise.
+
+
+
+ Sets taskbar state of the selected window.
+
+ Window to modify.
+ Progress sate to set.
+ if the operation succeeds. otherwise.
+
+
+
+ Sets taskbar value of the selected window.
+
+ Window to modify.
+ Progress sate to set.
+ Current value to display.
+ Maximum number for division.
+ if the operation succeeds. otherwise.
+
+
+
+ Sets taskbar value of the selected window.
+
+ Window to modify.
+ Current value to display.
+ Maximum number for division.
+ if the operation succeeds. otherwise.
+
+
+
+ Represents a contract with a service that provides tools for manipulating the theme.
+
+
+
+
+ Gets current application theme.
+
+ Currently set application theme.
+
+
+
+ Gets current system theme.
+
+ Currently set Windows theme.
+
+
+
+ Gets current system theme.
+
+ Currently set Windows theme using system enumeration.
+
+
+
+ Sets current application theme.
+
+ Theme type to set.
+ if the operation succeeds. otherwise.
+
+
+
+ Sets currently used Windows OS accent.
+
+ if the operation succeeds. otherwise.
+
+
+
+ Sets current application accent.
+
+ if the operation succeeds. otherwise.
+
+
+
+ Sets current application accent.
+
+ if the operation succeeds. otherwise.
+
+
+
+ Provides a dictionary implementation that contains WPF UI controls resources used by components and other elements of a WPF application.
+
+
+
+ <Application
+ xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
+ <Application.Resources>
+ <ResourceDictionary>
+ <ResourceDictionary.MergedDictionaries>
+ <ui:ControlsDictionary />
+ </ResourceDictionary.MergedDictionaries>
+ </ResourceDictionary>
+ </Application.Resources>
+ </Application>
+
+
+
+
+
+ Initializes a new instance of the class.
+ Default constructor defining of the WPF UI controls dictionary.
+
+
+
+
+ Custom design time attributes based on Marcin Najder implementation.
+
+
+
+ <ui:FluentWindow
+ xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
+ ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
+ ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}">
+ <Button Content="Hello World" />
+ </FluentWindow>
+
+
+
+
+
+ Gets a value indicating whether the framework is in design-time mode. (Caliburn.Micro implementation)
+
+
+
+ Helper for getting from .
+ to read from.
+ Background property value.
+
+
+ Helper for setting on .
+ to set on.
+ Background property value.
+
+
+ Helper for getting from .
+ to read from.
+ Foreground property value.
+
+
+ Helper for setting on .
+ to set on.
+ Foreground property value.
+
+
+
+ Custom which can provide .
+
+
+
+ <ui:Button
+ Appearance="Primary"
+ Content="WPF UI button with font icon"
+ Icon="{ui:FontIcon '🌈'}" />
+
+
+ <ui:Button Icon="{ui:FontIcon '🌈'}" />
+
+
+ <ui:HyperlinkButton Icon="{ui:FontIcon '🌈'}" />
+
+
+ <ui:TitleBar Icon="{ui:FontIcon '🌈'}" />
+
+
+
+
+
+ Custom which can provide .
+
+
+
+ <ui:Button
+ Appearance="Primary"
+ Content="WPF UI button with font icon"
+ Icon="{ui:ImageIcon '/my-icon.png'}" />
+
+
+ <ui:Button Icon="{ui:ImageIcon 'pack://application:,,,/Assets/wpfui.png'}" />
+
+
+ <ui:HyperlinkButton Icon="{ui:ImageIcon 'pack://application:,,,/Assets/wpfui.png'}" />
+
+
+ <ui:TitleBar Icon="{ui:ImageIcon 'pack://application:,,,/Assets/wpfui.png'}" />
+
+
+
+
+
+ Custom which can provide .
+
+
+
+ <ui:Button
+ Appearance="Primary"
+ Content="WPF UI button with font icon"
+ Icon="{ui:SymbolIcon Symbol=Fluent24}" />
+
+
+ <ui:Button Icon="{ui:SymbolIcon Fluent24}" />
+
+
+ <ui:HyperlinkButton Icon="{ui:SymbolIcon Fluent24}" />
+
+
+ <ui:TitleBar Icon="{ui:SymbolIcon Fluent24}" />
+
+
+
+
+
+ Collection of theme resources.
+
+
+
+ <ui:TextBox Foreground={ui:ThemeResource TextFillColorSecondaryBrush} />
+
+
+
+
+
+ Unspecified theme resource.
+
+
+
+
+ Gradient .
+
+
+
+
+ Gradient .
+
+
+
+
+ Gradient .
+
+
+
+
+ Class for Xaml markup extension for static resource references.
+
+
+
+ <ui:Button
+ Appearance="Primary"
+ Content="WPF UI button with font icon"
+ Foreground={ui:ThemeResource SystemAccentColorPrimaryBrush} />
+
+
+ <ui:TextBox Foreground={ui:ThemeResource TextFillColorSecondaryBrush} />
+
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class.
+ Takes the resource key that this is a static reference to.
+
+
+
+
+ Provides a dictionary implementation that contains WPF UI theme resources used by components and other elements of a WPF application.
+
+
+
+ <Application
+ xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml">
+ <Application.Resources>
+ <ResourceDictionary>
+ <ResourceDictionary.MergedDictionaries>
+ <ui:ThemesDictionary Theme = "Dark" />
+ </ResourceDictionary.MergedDictionaries>
+ </ResourceDictionary>
+ </Application.Resources>
+ </Application>
+
+
+
+
+
+ Sets the default application theme.
+
+
+
+
+ A service that provides methods related to navigation.
+
+
+
+
+ A service that provides methods related to navigation.
+
+
+
+
+ Gets or sets the control representing navigation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Set of properties used when creating a new simple content dialog.
+
+
+
+
+ Gets or sets a name at the top of the content dialog.
+
+
+
+
+ Gets or sets a message displayed in the content dialog.
+
+
+
+
+ Gets or sets the name of the button that closes the content dialog.
+
+
+
+
+ Gets or sets the default text of the primary button at the bottom of the content dialog.
+ If not added, or , it will not be displayed.
+
+
+
+
+ Gets or sets the default text of the secondary button at the bottom of the content dialog.
+ If not added, or , it will not be displayed.
+
+
+
+
+ A service that provides methods related to displaying the .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Allows you to manage the animations of the window icon in the taskbar.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Allows to change the status of the displayed notification in the application icon on the TaskBar.
+
+
+
+
+ Gets a value indicating whether the current operating system supports task bar manipulation.
+
+
+
+
+ Allows to change the status of the progress bar in the task bar.
+
+ Window to manipulate.
+ State of the progress indicator.
+
+
+
+ Allows to change the status of the progress bar in the task bar.
+
+ Window handle.
+ State of the progress indicator.
+
+
+
+ Allows to change the fill of the task bar.
+
+ Window to manipulate.
+ Progress sate to set.
+ Current value to display
+
+
+
+ Allows to change the fill of the task bar.
+
+ Window to manipulate.
+ Progress sate to set.
+ Current value to display
+ Total number for division.
+
+
+
+ Allows to change the fill of the task bar.
+
+ Window handle.
+ Progress sate to set.
+ Current value to display
+
+
+
+ Allows to change the fill of the task bar.
+
+ Window handle.
+ Progress sate to set.
+ Current value to display
+ Total number for division.
+
+
+
+ Specifies the state of the progress indicator in the Windows task bar.
+
+
+
+
+
+ No progress indicator is displayed in the task bar area.
+
+
+
+
+ A pulsing green (W10) or gray (W11) indicator is displayed in the task bar area.
+
+
+
+
+ A green progress indicator is displayed in the task bar area.
+
+
+
+
+ A red progress indicator is displayed in the task bar area.
+
+
+
+
+ A yellow progress indicator is displayed in the task bar area.
+
+
+
+
+ Lets you set the app theme.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Represents a UI application.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Gets a value indicating whether the application is running outside of the desktop app context.
+
+
+
+
+ Gets the current application.
+
+
+
+
+ Gets or sets the application's main window.
+
+
+
+
+ Gets or sets the application's resources.
+
+
+
+
+ Gets or sets the application's main window.
+
+
+
+
+ Turns the application's into shutdown mode.
+
+
+
+
+ Allows to get the WPF UI assembly through .
+
+
+
+
+ Gets the WPF UI assembly.
+
+
+
+
+ Common Window utilities.
+
+
+
+
+ Gets a value indicating whether the operating system is NT or newer.
+
+
+
+
+ Gets a value indicating whether the operating system version is greater than or equal to 6.0.
+
+
+
+
+ Gets a value indicating whether the operating system version is greater than or equal to 6.1.
+
+
+
+
+ Gets a value indicating whether the operating system version is greater than or equal to 6.2.
+
+
+
+
+ Gets a value indicating whether the operating system version is greater than or equal to 10.0* (build 10240).
+
+
+
+
+ Gets a value indicating whether the operating system version is greater than or equal to 10.0* (build 22000).
+
+
+
+
+ Gets a value indicating whether the operating system version is greater than or equal to 10.0* (build 22523).
+
+
+
+
+ Gets a value indicating whether the operating system version is greater than or equal to 10.0* (build 22557).
+
+
+
+
+ Gets a value indicating whether Desktop Window Manager (DWM) composition is enabled.
+
+
+
+
+ Tries to get the OS version from the Windows registry.
+
+
+
+
+ GeneratedInternalTypeHelper
+
+
+
+
+ CreateInstance
+
+
+
+
+ GetPropertyValue
+
+
+
+
+ SetPropertyValue
+
+
+
+
+ CreateDelegate
+
+
+
+
+ AddEventHandler
+
+
+
+
+ Specifies that null is allowed as an input even if the corresponding type disallows it.
+
+
+
+
+ Indicates that the specified method parameter expects a constant.
+
+
+ This can be used to inform tooling that a constant should be used as an argument for the annotated parameter.
+
+
+
+
+ Indicates the minimum bound of the expected constant, inclusive.
+
+
+
+
+ Indicates the maximum bound of the expected constant, inclusive.
+
+
+
+
+ Specifies that null is disallowed as an input even if the corresponding type allows it.
+
+
+
+
+ Applied to a method that will never return under any circumstance.
+
+
+
+
+ Specifies that the method will not return if the associated Boolean parameter is passed the specified value.
+
+
+
+
+ Initializes the attribute with the specified parameter value.
+
+
+ The condition parameter value. Code after the method will be considered unreachable
+ by diagnostics if the argument to the associated parameter matches this value.
+
+
+
+
+ Gets the condition parameter value.
+
+
+
+
+ Indicates that an API is experimental and it may change in the future.
+
+
+ This attribute allows call sites to be flagged with a diagnostic that indicates that an experimental
+ feature is used. Authors can use this attribute to ship preview features in their assemblies.
+
+
+
+
+ Initializes a new instance of the class,
+ specifying the ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+ The ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+
+
+ Gets the ID that the compiler will use when reporting a use of the API the attribute applies to.
+
+ The unique diagnostic ID.
+
+ The diagnostic ID is shown in build output for warnings and errors.
+ This property represents the unique ID that can be used to suppress the warnings or errors, if needed.
+
+
+
+
+ Gets or sets the URL for corresponding documentation.
+ The API accepts a format string instead of an actual URL, creating a generic URL that includes the diagnostic ID.
+
+ The format string that represents a URL to corresponding documentation.
+ An example format string is https://contoso.com/obsoletion-warnings/{0}.
+
+
+
+ Specifies that an output may be null even if the corresponding type disallows it.
+
+
+
+
+ Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it.
+
+
+
+
+ Initializes the attribute with the specified return value condition.
+
+ The return value condition. If the method returns this value, the associated parameter may be null.
+
+
+
+ Gets the return value condition.
+
+
+
+
+ Specifies that the method or property will ensure that the listed field and property members have not-null values.
+
+
+
+
+ Initializes the attribute with a field or property member.
+
+ The field or property member that is promised to be not-null.
+
+
+
+ Initializes the attribute with the list of field and property members.
+
+ The list of field and property members that are promised to be not-null.
+
+
+
+ Gets field or property member names.
+
+
+
+
+ Specifies that the method or property will ensure that the listed field and property
+ members have not-null values when returning with the specified return value condition.
+
+
+
+
+ Initializes the attribute with the specified return value condition and a field or property member.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+ The field or property member that is promised to be not-null.
+
+
+
+ Initializes the attribute with the specified return value condition and list of field and property members.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+ The list of field and property members that are promised to be not-null.
+
+
+
+ Gets the return value condition.
+
+
+
+
+ Gets field or property member names.
+
+
+
+
+ Specifies that an output will not be null even if the corresponding type allows it.
+ Specifies that an input argument was not null when the call returns.
+
+
+
+
+ Specifies that the output will be non-null if the named parameter is non-null.
+
+
+
+
+ Initializes the attribute with the associated parameter name.
+
+ The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
+
+
+
+ Gets the associated parameter name.
+
+
+
+
+ Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it.
+
+
+
+
+ Initializes the attribute with the specified return value condition.
+
+ The return value condition. If the method returns this value, the associated parameter will not be null.
+
+
+ Gets the return value condition.
+
+
+
+ Specifies that this constructor sets all required members for the current type,
+ and callers do not need to set any required members themselves.
+
+
+
+
+ Specifies the syntax used in a string.
+
+
+
+
+ Initializes the with the identifier of the syntax used.
+
+ The syntax identifier.
+
+
+ Initializes the with the identifier of the syntax used.
+ The syntax identifier.
+ Optional arguments associated with the specific syntax employed.
+
+
+ Gets the identifier of the syntax used.
+
+
+ Optional arguments associated with the specific syntax employed.
+
+
+ The syntax identifier for strings containing composite formats for string formatting.
+
+
+ The syntax identifier for strings containing date format specifiers.
+
+
+ The syntax identifier for strings containing date and time format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing JavaScript Object Notation (JSON).
+
+
+ The syntax identifier for strings containing numeric format specifiers.
+
+
+ The syntax identifier for strings containing regular expressions.
+
+
+ The syntax identifier for strings containing time format specifiers.
+
+
+ The syntax identifier for strings containing format specifiers.
+
+
+ The syntax identifier for strings containing URIs.
+
+
+ The syntax identifier for strings containing XML.
+
+
+
+ Used to indicate a byref escapes and is not scoped.
+
+
+
+ There are several cases where the C# compiler treats a as implicitly
+ - where the compiler does not allow the to escape the method.
+
+
+ For example:
+
+ - for instance methods.
+ - parameters that refer to types.
+ - parameters.
+
+
+
+ This attribute is used in those instances where the should be allowed to escape.
+
+
+ Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for
+ API authors to understand the lifetime implications of applying this attribute and how it may impact their users.
+
+
+
+
+ Represent a type can be used to index a collection either from the start or the end.
+
+ Index is used by the C# compiler to support the new index syntax
+
+ int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
+ int lastElement = someArray[^1]; // lastElement = 5
+
+
+
+
+ Construct an Index using a value and indicating if the index is from the start or from the end.
+ The index value. it has to be zero or positive number.
+ Indicating if the index is from the start or from the end.
+
+ If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
+
+
+
+ Create an Index pointing at first element.
+
+
+ Create an Index pointing at beyond last element.
+
+
+ Create an Index from the start at the position indicated by the value.
+ The index value from the start.
+
+
+ Create an Index from the end at the position indicated by the value.
+ The index value from the end.
+
+
+ Returns the index value.
+
+
+ Indicates whether the index is from the start or the end.
+
+
+ Calculate the offset from the start using the giving collection length.
+ The length of the collection that the Index will be used with. length has to be a positive value
+
+ For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
+ we don't validate either the returned offset is greater than the input length.
+ It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
+ then used to index a collection will get out of range exception which will be same affect as the validation.
+
+
+
+ Indicates whether the current Index object is equal to another object of the same type.
+ An object to compare with this object
+
+
+ Indicates whether the current Index object is equal to another Index object.
+ An object to compare with this object
+
+
+ Returns the hash code for this instance.
+
+
+ Converts integer number to an Index.
+
+
+ Converts the value of the current Index object to its equivalent string representation.
+
+
+ Represent a range has start and end indexes.
+
+ Range is used by the C# compiler to support the range syntax.
+
+ int[] someArray = new int[5] { 1, 2, 3, 4, 5 };
+ int[] subArray1 = someArray[0..2]; // { 1, 2 }
+ int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
+
+
+
+
+ Represent the inclusive start index of the Range.
+
+
+ Represent the exclusive end index of the Range.
+
+
+ Construct a Range object using the start and end indexes.
+ Represent the inclusive start index of the range.
+ Represent the exclusive end index of the range.
+
+
+ Indicates whether the current Range object is equal to another object of the same type.
+ An object to compare with this object
+
+
+ Indicates whether the current Range object is equal to another Range object.
+ An object to compare with this object
+
+
+ Returns the hash code for this instance.
+
+
+ Converts the value of the current Range object to its equivalent string representation.
+
+
+ Create a Range object starting from start index to the end of the collection.
+
+
+ Create a Range object starting from first element in the collection to the end Index.
+
+
+ Create a Range object starting from first element to the end.
+
+
+ Calculate the start offset and length of range object using a collection length.
+ The length of the collection that the range will be used with. length has to be a positive value.
+
+ For performance reason, we don't validate the input length parameter against negative values.
+ It is expected Range will be used with collections which always have non negative length/count.
+ We validate the range is inside the length scope though.
+
+
+
+
+ Indicates the type of the async method builder that should be used by a language compiler to
+ build the attributed async method or to build the attributed type when used as the return type
+ of an async method.
+
+
+
+ Initializes the .
+ The of the associated builder.
+
+
+ Gets the of the associated builder.
+
+
+
+ An attribute that allows parameters to receive the expression of other parameters.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The condition parameter value.
+
+
+
+ Gets the parameter name the expression is retrieved from.
+
+
+
+
+ Initialize the attribute to refer to the method on the type.
+
+ The type of the builder to use to construct the collection.
+ The name of the method on the builder to use to construct the collection.
+
+ must refer to a static method that accepts a single parameter of
+ type and returns an instance of the collection being built containing
+ a copy of the data from that span. In future releases of .NET, additional patterns may be supported.
+
+
+
+
+ Gets the type of the builder to use to construct the collection.
+
+
+
+
+ Gets the name of the method on the builder to use to construct the collection.
+
+
+ This should match the metadata name of the target method.
+ For example, this might be ".ctor" if targeting the type's constructor.
+
+
+
+
+ Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
+
+
+
+
+ Creates a new instance of the type.
+
+ The name of the feature to indicate.
+
+
+
+ The name of the compiler feature.
+
+
+
+
+ If true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand .
+
+
+
+
+ The used for the ref structs C# feature.
+
+
+
+
+ The used for the required members C# feature.
+
+
+
+
+ Indicates which arguments to a method involving an interpolated string handler should be passed to that handler.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The name of the argument that should be passed to the handler.
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Initializes a new instance of the class.
+
+ The names of the arguments that should be passed to the handler.
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Gets the names of the arguments that should be passed to the handler.
+
+ may be used as the name of the receiver in an instance method.
+
+
+
+ Indicates the attributed type is to be used as an interpolated string handler.
+
+
+
+
+ Reserved to be used by the compiler for tracking metadata.
+ This class should not be used by developers in source code.
+
+
+
+
+ Used to indicate to the compiler that a method should be called
+ in its containing module's initializer.
+
+
+ When one or more valid methods
+ with this attribute are found in a compilation, the compiler will
+ emit a module initializer which calls each of the attributed methods.
+
+ Certain requirements are imposed on any method targeted with this attribute:
+ - The method must be `static`.
+ - The method must be an ordinary member method, as opposed to a property accessor, constructor, local function, etc.
+ - The method must be parameterless.
+ - The method must return `void`.
+ - The method must not be generic or be contained in a generic type.
+ - The method's effective accessibility must be `internal` or `public`.
+
+ The specification for module initializers in the .NET runtime can be found here:
+ https://github.com/dotnet/runtime/blob/main/docs/design/specs/Ecma-335-Augments.md#module-initializer
+
+
+
+
+ Specifies the priority of a member in overload resolution. When unspecified, the default priority is 0.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The priority of the attributed member. Higher numbers are prioritized, lower numbers are deprioritized. 0 is the default if no attribute is present.
+
+
+
+ The priority of the member.
+
+
+
+
+ Indicates that a method will allow a variable number of arguments in its invocation.
+
+
+
+
+ Specifies that a type has required members or that a member is required.
+
+
+
+
+ Reserved for use by a compiler for tracking metadata.
+ This attribute should not be used by developers in source code.
+
+
+
+
+ Used to indicate to the compiler that the .locals init flag should not be set in method headers.
+
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Initializes a new instance of the class with the specified message.
+
+ An optional message associated with this attribute instance.
+
+
+
+ Returns the optional message associated with this attribute instance.
+
+
+
+
+ Returns the optional URL associated with this attribute instance.
+
+
+
+