Compare commits

...
394 Commits
Author SHA1 Message Date
dyoder 819b8d72f0 added Upload-File.ps1 2026-10-01 11:16:36 -04:00
dyoder a53dcfb6a9 added boolean conversion helper and replaced code in scripts to use it 2026-10-01 11:16:23 -04:00
dyoder 2efb7116d8 removed, not necessary 2026-10-01 11:14:25 -04:00
dyoder 3b8ceeac5a added RMM shim script to repo 2026-10-01 09:39:44 -04:00
dyoder 4912bbc28e change from domain so email notifications pass through relay after successful upload 2026-10-01 09:36:23 -04:00
dyoder 0975f5d24a implement new Download-FileDirectly function to handle downloads 2026-09-30 11:42:05 -04:00
dyoder 4e06ede640 added Test-FileDownload.ps1 2026-09-30 10:34:09 -04:00
dyoder 4e6c35a8f4 stop on BITS download errors
reject 0 byte dsa-collector.exe before launching
2026-09-30 09:46:37 -04:00
dyoder 35c1904d0d add upload feature to DSA manifest creation 2026-09-30 08:12:55 -04:00
dyoder c74fe82d36 add Upload-File function to Tools.ps1 2026-09-30 07:45:01 -04:00
dyoder 745902f8c2 accept non-boolean string values for the boolean $ZIP
append hostname to output file
2026-09-30 07:20:37 -04:00
dyoder bb44f2a9ef added Get-DSAManifest.ps1 2026-09-30 07:00:20 -04:00
dyoder a47be2f91b change hosting location for ODT setup file 2026-09-21 21:59:47 -04:00
dyoder 7a25ff2d6d update tool hosting location 2026-09-21 15:05:32 -04:00
dyoder 164dc1ac58 update to version 8.7.1.177 2026-09-16 12:37:02 -04:00
dyoder 1732f23d37 update to version 8.7.0.224 2026-08-26 17:24:29 -04:00
dyoder af1ccf5fa1 added Install-SystemFont 2026-07-24 14:43:37 -04:00
dyoder 1b68d12ab3 revert test commit after gitea upgrade 2026-07-16 17:44:17 -04:00
dyoder f5e12804be test commit after gitea upgrade 2026-07-16 17:43:45 -04:00
dyoder 912bff8d81 added context for agents 2026-07-16 10:42:41 -04:00
dyoder 8a53de4430 update and harden 2026-07-16 10:42:30 -04:00
dyoder 1e5739b75d replace bottom table with list of unlocked times 2026-07-08 16:51:52 -04:00
dyoder db8fc541df more clarity 2026-07-08 16:44:12 -04:00
dyoder faddaba0ed improved clarity 2026-07-08 12:20:45 -04:00
dyoder 8ae07c0030 more fixes 2026-07-08 12:13:48 -04:00
dyoder 4a7e0642ef more fixes 2026-07-08 12:00:25 -04:00
dyoder 3470464b1d fix an error 2026-07-08 11:56:10 -04:00
dyoder cf40e50c30 behavioral changes 2026-07-08 11:36:27 -04:00
dyoder ac85a06003 add usage window 2026-07-08 11:27:51 -04:00
dyoder cce2950d10 add additional indicators 2026-07-08 11:04:22 -04:00
dyoder 41260f438b added source column 2026-07-07 17:30:11 -04:00
dyoder 1e10306b61 add power events 2026-07-07 15:39:57 -04:00
dyoder 631ce956fd fixed stability and output 2026-07-07 15:23:15 -04:00
dyoder 06b66813cf fix: check both target and subject fields, 4624 type 7 is now an unlock event, 4634 now allows type 7 for logoff/session change, added security log access check 2026-07-07 15:06:27 -04:00
dyoder 5fa2d50bd6 added default run mode when variables are missing or not defined 2026-07-07 14:56:56 -04:00
dyoder 26191c9e58 added Get-UserLogonActivity.ps1 2026-07-07 14:45:18 -04:00
dyoder 91c3bc858d added Create-NetworkDrive 2026-06-26 10:35:40 -04:00
dyoder 9d36fabf00 fix: improve MSP360 module import and installation process 2026-05-29 11:24:53 -04:00
dyoder d2824cb771 changed URL because git.io is commonly blocked 2026-05-18 16:38:50 -04:00
dyoder 30fe5e32d6 update to version 8.6.0.338 2026-05-18 16:30:38 -04:00
dyoder e7fe443894 update to version 2.3.0 2026-03-17 13:19:24 -04:00
dyoder f446086e38 upgrade to version 3.0.37 2026-03-17 11:53:30 -04:00
dyoder 30c68220f2 fix: correct path for deleting old version of ekDNSHelper 2026-03-04 23:16:04 -05:00
dyoder a17848a5eb fix: improve error handling for ekDNSHelper version check 2026-03-04 23:12:43 -05:00
dyoder 930c9eca73 fix: update required version of ekDNSHelper to 2.2.0 2026-03-04 23:00:20 -05:00
dyoder cbee4335f3 fix: improve syntax for Test-Path and Remove-Item in ekDNSHelper deletion logic 2026-03-04 22:55:16 -05:00
dyoder 751af98c6e fix: correct syntax for Remove-Item path in ekDNSHelper deletion logic 2026-03-04 22:51:45 -05:00
dyoder 7a74d6d2a9 fix: enhance logic to check and download the correct version of ekDNSHelper 2026-03-04 22:49:05 -05:00
dyoder 79290125f4 fix: add logic to delete old version of ekDNSHelper if it exists 2026-03-04 18:14:43 -05:00
dyoder ac870b4e82 fix: correct syntax for Test-Path condition in DNS helper URL selection 2026-03-04 18:11:28 -05:00
dyoder a9ff804048 fix: update DNS helper executable references and remove unused resolver logic 2026-03-04 17:59:33 -05:00
dyoder 330501c551 Update to version 8.3.0.85 2026-02-13 14:14:52 -05:00
dyoder b55d302311 fix: rename 'toc' key to 'table_of_contents' for clarity 2026-01-14 13:55:07 -05:00
dyoder 3b4532529b update to version 8.2.0.122 2025-12-16 13:06:21 -05:00
dyoder 253b714fe2 fix: update profile removal logic to use SID variable directly 2025-10-08 18:07:07 -04:00
dyoder c6d587db8e refactor removal process for provisioning admin account and profile 2025-10-08 18:05:59 -04:00
dyoder 5da50abc5a update provisioning admin username to "ek-provadmin" and add domain controller check 2025-10-08 17:44:17 -04:00
dyoder 1c50712eb0 add script to remove provisioning admin user and associated profile 2025-10-08 17:27:07 -04:00
dyoder c6bd2e7c49 update provisioning admin username to match project standards 2025-10-08 17:22:12 -04:00
dyoder 04d714cc5b fix error handling in Set-DefaultAdministratorCredentials script 2025-10-08 17:19:41 -04:00
dyoder 31312fb510 update provisioning admin username to match standard naming convention 2025-10-08 17:18:06 -04:00
dyoder f2b6a44169 add script to remove provisioning admin user and associated profile 2025-10-08 16:14:29 -04:00
dyoder 1c582e7b0d refactor New-LocalAccount function to accept plain text password and hide user from login screen 2025-10-08 16:12:56 -04:00
dyoder baa6f76e1a clean exit on successful install 2025-10-08 11:40:21 -04:00
dyoder a378cf2cc0 initial commit 2025-10-03 14:32:21 -04:00
dyoder 93f916e48a update the script to check for admin, not enforce 2025-09-24 14:57:48 -04:00
dyoder 6abcc7a729 hopefully the last minor wording update 2025-09-24 14:37:34 -04:00
dyoder 9781af8745 another minor wording update 2025-09-24 14:35:40 -04:00
dyoder d6653c4554 minor wording change 2025-09-24 14:34:36 -04:00
dyoder d4acdaae9e minor 2025-09-24 14:30:31 -04:00
dyoder 1aab6b3755 initial commit 2025-09-24 14:25:29 -04:00
dyoder 227f940ca1 remove duplicated functions from Tools.ps1 2025-08-15 16:02:45 -04:00
dyoder 821505014d cleanup recycle bin cleaner 2025-08-15 16:00:11 -04:00
dyoder 3f89290295 fix old downloaded apps cleaner 2025-08-15 15:57:59 -04:00
dyoder 432133775a stop shadow copy cleaner from showing error on 0 shadows 2025-08-15 15:55:11 -04:00
dyoder 49b0f25369 source Tools.ps1 again 2025-08-15 15:48:50 -04:00
dyoder c3a89f73bb fix cleaners for recycle bin and autodesk installers 2025-08-15 15:48:16 -04:00
dyoder 1036cca39a update to version 8.1.2.172 2025-08-13 16:15:28 -04:00
dyoder 92c75d8f22 update to version 8.1.0.630 2025-05-22 12:47:02 -04:00
dyoder 7193b6e596 update to version 8.1.0.607 2025-05-05 16:25:27 -04:00
dyoder 0fededf036 use cmdlet name instead of alias 2025-04-11 17:22:26 -04:00
dyoder 910cc65444 match 2025-03-22 08:20:13 -04:00
dyoder b3ebdd4434 temporarily remove installation of local tools 2025-03-22 08:19:55 -04:00
dyoder 61b0c676bc update to version 8.0.4.6 2025-02-07 16:53:49 -05:00
dyoder 0cbc618d05 update to version 8.0.3.36 2025-01-22 09:44:08 -05:00
dyoder af7c4af9b6 fix several places where full path was not provided to Remove-Item 2025-01-06 13:40:15 -05:00
dyoder 65f8e2c6a1 replace Get-ItemPropertyValue with Get-ItemProperty for PS 3.0 compatibility 2025-01-06 13:17:25 -05:00
dyoder 8fca4894cd replace Get-ItemPropertyValue with Get-ItemProperty for PS 3.0 compatibility 2025-01-06 13:16:48 -05:00
dyoder 4a5043ca74 added IsRunning func from Tools.ps1 2025-01-06 12:39:06 -05:00
dyoder 8ace7c6be1 added Get-UserProfiles func from Tools.ps1 2025-01-06 12:35:23 -05:00
dyoder 288a1a4d9c don't source Tools.ps1 2025-01-06 12:32:17 -05:00
dyoder fdc0e536cc update DaysWithoutModification to DaysWithNoModification 2025-01-02 17:00:29 -05:00
dyoder 39ae5a4235 use Where-Object instead of Where
add found items to output
2025-01-02 16:52:59 -05:00
dyoder ff6d6bb88c initial commit 2025-01-02 16:27:53 -05:00
dyoder bbc6cdb142 update to version 8.0.2.41 2024-12-27 11:22:00 -05:00
dyoder 01c06ad64b removed 2024-12-06 11:12:56 -05:00
dyoder 38d0e87a2e initial commit 2024-12-06 11:12:13 -05:00
dyoder 5bc1437398 initial commit 2024-12-06 11:11:51 -05:00
dyoder cf575be0c6 updated to version 8.0.2.33 2024-12-05 09:56:52 -05:00
dyoder 20408316df adjusted comments 2024-12-05 09:56:09 -05:00
dyoder b8758fec60 initial commit 2024-12-05 09:54:48 -05:00
dyoder 7a91bce87a set default TLS version to 1.2 2024-11-06 10:07:39 -05:00
dyoder 6dc5a4efce renamed script 2024-10-22 14:34:31 -04:00
dyoder ececf83389 initial commit 2024-10-22 14:32:46 -04:00
dyoder cec26755f9 delete the correct shadow copies 2024-10-14 12:57:41 -04:00
dyoder 63792a557f match os edition instead 2024-09-19 15:05:29 -04:00
dyoder 87f7a75c9c exit early if run on a server 2024-09-19 14:50:35 -04:00
dyoder 5676dbb554 initial commit 2024-09-19 14:41:19 -04:00
dyoder 8e0e3711f7 Set-OutlookProfilePrompt 2024-08-23 11:18:44 -04:00
dyoder efea29ac95 fix casing for supplied path 2024-08-08 15:36:26 -04:00
dyoder 11317ef618 try using a literal path 2024-08-08 13:13:54 -04:00
dyoder 644cd0cd14 oops 2024-08-08 12:43:09 -04:00
dyoder b0772bcb1c cleanup the path the user enters 2024-08-08 12:41:58 -04:00
dyoder b8d9b7b9d3 don't operate on unset variables! 2024-08-06 15:00:13 -04:00
dyoder 98565b7fff apparently logging is no longer a config item in odt 2024-08-06 13:58:27 -04:00
dyoder 67d291e2eb don't need to see the UI if running as admin 2024-08-06 11:38:35 -04:00
dyoder 8d2913cd5a I don't think the strings need to be escaped 2024-08-06 11:37:05 -04:00
dyoder 4b740b06e8 initial commit 2024-08-06 11:27:46 -04:00
dyoder a447ac5652 update to v7.9.7.69 2024-08-01 14:47:43 -04:00
dyoder 298b8ef050 exit early if the BSOD report is empty 2024-07-18 15:42:41 -04:00
dyoder 2885d975f4 added func Get-StringHash 2024-07-15 13:30:29 -04:00
dyoder 659f03bf3a update to version 7.9.6.150 2024-07-05 12:21:03 -04:00
dyoder 96c3e03b89 bill ticket 2024-07-03 17:09:35 -04:00
dyoder e872cdf02a do not bill ticket 2024-07-03 17:09:29 -04:00
dyoder 6904647871 improved logic for sfc output 2024-07-03 17:00:42 -04:00
dyoder 2dd6384c57 exit if specific apps are running at the time of install 2024-07-03 16:53:29 -04:00
dyoder dbb6fefa07 cleanup chocolatey cache 2024-07-03 16:37:56 -04:00
dyoder bcf8b87dc5 update to version 4.0.2.11 2024-07-03 15:56:09 -04:00
dyoder 689cfa66df added invoking script name to local script file 2024-07-01 11:26:12 -04:00
dyoder 0127474dbe try getting the date again 2024-07-01 11:24:24 -04:00
dyoder bb682a43ee get the date to run and add a new line 2024-07-01 11:22:17 -04:00
dyoder 98c1b3e214 create script and run that instead 2024-07-01 11:19:20 -04:00
dyoder f77479c7b7 lots of cleanup 2024-06-25 16:07:23 -04:00
dyoder 68114af27e removed a few lines since the logic moved to Tools.ps1 2024-06-25 16:07:04 -04:00
dyoder f4905fec68 added a bunch of functions 2024-06-25 16:06:37 -04:00
dyoder b82f741a79 just need to test the data that gets sent to this script 2024-06-25 12:04:42 -04:00
dyoder 676dd0011c fixed incorrect global var name 2024-06-25 11:47:03 -04:00
dyoder 6c27d5fa18 source external scripts that have no arguments instead of running in scriptblock 2024-06-24 17:06:45 -04:00
dyoder c753b13f1e use new funcs in Tools.ps1 2024-06-24 16:51:50 -04:00
dyoder b5bfe4b814 use new funcs in Tools.ps1 2024-06-24 16:51:40 -04:00
dyoder 907ee882bf added funcs for working with tickets 2024-06-24 16:51:19 -04:00
dyoder cbdca2b4eb handle no alert data 2024-06-21 17:04:17 -04:00
dyoder 7986d28dfc renamed 2024-06-21 17:01:38 -04:00
dyoder 4e85dee127 import the syncro module if needed 2024-06-21 14:51:19 -04:00
dyoder b738b25b9a trim 2024-06-21 14:28:50 -04:00
dyoder ebc98e979e allow running the script adhoc to update an existing ticket 2024-06-21 14:24:37 -04:00
dyoder ea6f7cd489 fix ticket comment wording 2024-06-19 13:34:25 -04:00
dyoder dff96bbd12 added more ticket comments
tried to fix the regex for reducing sfc output
2024-06-19 13:29:59 -04:00
dyoder c078638e1d wait 20min for bsod report to be generated
don't download BlueScreenView, expect it
move all of ticket creation into try block
2024-06-17 17:04:16 -04:00
dyoder 05da2d726e comment out unused funcs 2024-06-17 16:48:50 -04:00
dyoder 427e694fb3 added func Install-LocalTools
run Install-LocalTools at every call to this script
2024-06-17 16:45:21 -04:00
dyoder 475f517417 initial commit 2024-06-17 15:03:47 -04:00
dyoder 16e8701bfc try to cleanup sfc output in ticket comment 2024-06-17 14:13:37 -04:00
dyoder 2ae279c429 added billing note for time entry 2024-06-14 21:29:06 -04:00
dyoder 5a03788ca2 cleanup output for ticket comments 2024-06-14 21:22:16 -04:00
dyoder cc3f42b539 update ticket subject to be user facing
added $TimeAttributedToUser for billing
delete Fix-WindowsHealth script after running
2024-06-14 17:09:27 -04:00
dyoder f82ce18319 little cleanup
added some pauses to prevent spamming the end user
invokes Fix-WindowsHealth if necessary
2024-06-14 16:42:50 -04:00
dyoder 4c32fe1c1e allow for updating ticket comments 2024-06-14 16:41:02 -04:00
dyoder 5b4c3f2f7a testing 2024-06-14 15:01:46 -04:00
dyoder 35b8e4bbbb a little cleanup 2024-06-14 13:51:56 -04:00
dyoder f07edb4044 use .net to read report into string var 2024-06-14 13:47:37 -04:00
dyoder 4c2e9979f1 correctly get string from file content 2024-06-14 13:37:29 -04:00
dyoder 4275e667df testing 2024-06-14 13:34:49 -04:00
dyoder ad0cfb45f3 testing 2024-06-14 13:32:10 -04:00
dyoder a425a38c25 don't download util twice
wait for ticket to be created before adding comment
2024-06-14 12:59:17 -04:00
dyoder 3f45c70b3a initial commit 2024-06-14 12:46:02 -04:00
dyoder 04b7ba11c6 update to v7.9.5.150 2024-06-12 09:51:28 -04:00
dyoder 3cb28f273e initial commit 2024-06-07 14:30:07 -04:00
dyoder f9f7045912 removed code to reboot into safe mode
minor changes to messages
2024-05-15 11:51:54 -04:00
dyoder b694033ace added backup service
simplified commands to run
2024-05-15 11:43:05 -04:00
dyoder f7fd81c048 fix multi-line command pipe 2024-05-09 17:00:20 -04:00
dyoder 6941d7aad4 correctly detect safe mode with networking
update ESET Uninstaller download link
2024-04-23 22:55:57 -04:00
dyoder 4cec14c893 fix detecting safe mode with networking 2024-04-23 11:36:37 -04:00
dyoder 33960214fd initial commit 2024-04-22 22:27:37 -04:00
dyoder ecc9ebcc72 fix rebooting into normal mode 2024-04-22 16:18:27 -04:00
dyoder 27a12a2b02 fix normal mode restart 2024-04-22 15:46:26 -04:00
dyoder 3904441949 better reboot 2024-04-22 12:48:40 -04:00
dyoder af90aba365 launch exe better 2024-04-22 12:44:11 -04:00
dyoder 20cefe10b6 initial commit 2024-04-22 11:31:28 -04:00
dyoder 7c95440fd4 changed from current to default boot option 2024-04-22 11:31:16 -04:00
dyoder 92b603c312 add cleanup step 2024-04-16 12:38:12 -04:00
dyoder 1388b9b35d incorrect param 2024-04-16 12:35:46 -04:00
dyoder 55985e0a3e testing 2024-04-16 12:34:34 -04:00
dyoder 9cf6a0a7e8 update for exe installer 2024-04-16 12:29:18 -04:00
dyoder c3b9bb9a64 update to version 4.0.0.579 2024-04-16 12:06:44 -04:00
dyoder 10a1b2affb forgot to rename some variables 2024-03-29 11:51:34 -04:00
dyoder 75d2f83d3a workaround for long url 2024-03-29 11:48:18 -04:00
dyoder 7bf14c30b5 initial commit 2024-03-13 15:12:24 -04:00
dyoder c33adb91bd reinstall if errors are found in status log 2024-03-08 20:39:22 -05:00
dyoder dab76d733c I think this is it 2024-02-28 15:23:06 -05:00
dyoder 54510e3007 hopefully this is the last test 2024-02-28 14:18:09 -05:00
dyoder 5167cdcd76 reduce some variables 2024-02-28 14:14:23 -05:00
dyoder 80877ded48 ... 2024-02-28 14:10:55 -05:00
dyoder 71d1b77457 again 2024-02-28 14:09:44 -05:00
dyoder c906da8ee3 this is not the way... 2024-02-28 14:08:20 -05:00
dyoder 3bb28f34c1 testing more errors 2024-02-28 14:05:46 -05:00
dyoder a3bd47580a testing errors 2024-02-28 14:04:06 -05:00
dyoder 5e0e080be5 use int instead of timespan 2024-02-28 14:01:00 -05:00
dyoder 4c5f608457 initial commit 2024-02-28 13:51:44 -05:00
dyoder 098c590084 print message if no battery detected 2024-02-27 11:09:13 -05:00
dyoder 09fdfa5109 a little more specific 2024-02-26 16:27:58 -05:00
dyoder 175248ec4d better profile path selection 2024-02-26 16:10:34 -05:00
dyoder 497126d138 minor updates 2024-02-23 17:05:49 -05:00
dyoder 401cc3ded1 remove testing stuff 2024-02-23 15:44:28 -05:00
dyoder 70aca302a8 work with RAW backup plan 2024-02-23 15:43:59 -05:00
dyoder 4a11d3a924 initial commit 2024-02-23 14:37:03 -05:00
dyoder cbef3ea79d delete existing reports with same name 2024-02-23 11:52:57 -05:00
dyoder 9f926a0322 initial commit 2024-02-23 11:49:12 -05:00
dyoder 9b4bd1bcd9 added Get-LongDate func 2024-02-23 11:15:00 -05:00
dyoder 3ff21ab8f0 print sorted list of $IncludedPaths 2024-02-23 00:44:02 -05:00
dyoder 038b8f2685 better handle missing MaxFileSize prop 2024-02-23 00:30:42 -05:00
dyoder dfb8a3eccc also get paths from newer plan format 2024-02-22 23:52:39 -05:00
dyoder f31eb48556 more testing 2024-02-22 23:32:45 -05:00
dyoder 71affea1fb now it will output a newline 2024-02-22 22:52:37 -05:00
dyoder 4b807a6f7f maybe this will make it work? 2024-02-22 17:43:40 -05:00
dyoder 1be2362045 initial commit 2024-02-22 17:39:44 -05:00
dyoder 51f0b72fd9 update to version 7.9.3.139 2024-02-21 16:53:28 -05:00
dyoder d43c724cd5 make inline process a variable 2024-02-01 11:08:42 -05:00
dyoder d542a56ed7 minor changes 2024-02-01 11:03:10 -05:00
dyoder 76f03b124d minor changes 2024-02-01 11:02:16 -05:00
dyoder 637f5f95f1 fixed link to vm agent installer 2024-01-29 16:46:50 -05:00
dyoder ec6a9a96ec use master password to change agent settings 2024-01-29 11:58:20 -05:00
dyoder 41b7174628 remove an old comment 2024-01-26 16:36:08 -05:00
dyoder ec568adef4 renamed 2024-01-26 11:51:22 -05:00
dyoder b0223ddaa9 initial commit 2024-01-26 11:47:36 -05:00
dyoder 94d47954cd guard to exit script if agent is installed 2024-01-19 21:39:28 -05:00
dyoder 60df390c1b guard to exit script if agent is installed 2024-01-19 21:39:13 -05:00
dyoder d7e93bbe14 update to version 7.9.2.266 2024-01-16 11:22:46 -05:00
dyoder c087522e0a update to version 3.5.6 2024-01-11 14:06:04 -05:00
dyoder c8e45b62dd initial commit 2024-01-05 14:14:45 -05:00
dyoder 9aa3946c06 don't print out empty items 2024-01-04 09:26:05 -05:00
dyoder 9153541837 fix the issue 2024-01-04 09:13:26 -05:00
dyoder 5b43b97ce2 prevent multiple packages from getting returned 2024-01-04 08:51:49 -05:00
dyoder ff522d1e79 update to version 7.9.2.244 2023-12-29 14:10:47 -05:00
dyoder 1f21da9b19 update for Syncro 2023-12-15 13:07:33 -05:00
dyoder cb8603c2f8 added a few dell apps to remove 2023-12-15 10:07:24 -05:00
dyoder 1cdbf5ee9a remove fix for previous rmm 2023-12-07 23:53:21 -05:00
dyoder b698f4337d update 2023-12-07 12:06:38 -05:00
dyoder 1f69a746c9 update 2023-12-07 12:06:28 -05:00
dyoder c94b375aec break apart long line 2023-12-07 10:38:45 -05:00
dyoder 8a12219565 more fixes 2023-12-05 14:59:17 -05:00
dyoder 3e8def09d6 ren New-LocalUser to New-LocalAccount
minor updates to New-LocalAccount
2023-12-05 14:56:12 -05:00
dyoder aa807bf51d update to v7.9.1.152 2023-12-05 14:28:55 -05:00
dyoder d37faa57dc remove more than 1 shadow 2023-11-09 13:34:09 -05:00
dyoder 2c35b31599 added output 2023-11-09 12:21:21 -05:00
dyoder ea6406e38f remove oldest shadow copy 2023-11-09 12:20:20 -05:00
dyoder 0aa95ef948 output to console only 2023-11-09 11:20:10 -05:00
dyoder d35ff03237 rename Uninstall-MSI func for prod 2023-11-06 14:36:41 -05:00
dyoder 8a6067d653 minor changes 2023-11-01 21:14:33 -04:00
dyoder 4e1ca02189 note to change func name 2023-11-01 21:09:14 -04:00
dyoder 232b7ce3ea rename Uninstall-MSI to Test-Uninstall-MSI
additional testing necessary before prod
2023-11-01 21:06:11 -04:00
dyoder 14618526a7 emergency fix for Uninstall-MSI 2023-11-01 21:04:52 -04:00
dyoder 81bbde43ab fix Expand-Archive 2023-11-01 15:51:24 -04:00
dyoder 7a89f0b85e switch to BITS for file download 2023-11-01 15:47:27 -04:00
dyoder 81a48222ad umm... 2023-11-01 15:36:58 -04:00
dyoder 048dfa839c get rid of Tools.ps1 call 2023-11-01 15:33:03 -04:00
dyoder 15ce0f5c4b remove con out in Download-File 2023-11-01 15:29:07 -04:00
dyoder 8a6d597984 update comment url 2023-11-01 15:19:22 -04:00
dyoder aaae75eb9b update to version 2.3.0.692 2023-11-01 15:16:39 -04:00
dyoder 9a94618799 possibly an emergency 2023-11-01 14:30:39 -04:00
dyoder 7b77fc85d9 small fix to make usable again 2023-11-01 13:37:57 -04:00
dyoder 48c4abb60a updates to Uninstall-MSI 2023-10-19 11:38:55 -04:00
dyoder 589055732e updated comment 2023-10-19 11:37:59 -04:00
dyoder a1a03c4c01 removed 2023-10-19 11:37:42 -04:00
dyoder a97150d45a version lock installer
version 2023.1.340.117
2023-10-13 10:55:09 -04:00
dyoder f46446ae37 added func Install-MSI 2023-10-12 22:11:17 -04:00
dyoder 4e3b7e4683 few fixes and new func IsURL 2023-10-12 22:00:22 -04:00
dyoder 45b3b17ac7 get absolute uri 2023-10-12 13:54:58 -04:00
dyoder feaab79d35 updated Get-UserProfiles func 2023-10-06 16:14:34 -04:00
dyoder 06fdd45c36 overhaul on zip funcs 2023-10-06 16:06:59 -04:00
dyoder 25efd5ef03 use updated func 2023-10-06 16:06:44 -04:00
dyoder 8fa1f1ef61 adjusted for removal of Remove-File cmdlet 2023-10-06 14:16:19 -04:00
dyoder 7f98291e12 update to use new funcs 2023-10-06 13:04:29 -04:00
dyoder 4cc7e91722 too many updates 2023-10-06 13:04:09 -04:00
dyoder cfa87fc674 removed for later rework 2023-10-05 17:04:53 -04:00
dyoder 9409f000e0 use new func in Tools.ps1 to get random file 2023-10-05 14:23:06 -04:00
dyoder 0499a143ef added 2 new funcs for getting temp files 2023-10-05 14:22:37 -04:00
dyoder 5cd4e99465 very minor changes 2023-10-05 14:10:02 -04:00
dyoder 8962142931 Write-Output/Error is streamlined
and will send output to the console
2023-10-05 14:09:27 -04:00
dyoder bb18c3436d added note for Write-Output func 2023-10-05 13:39:07 -04:00
dyoder 72445c1701 remove alias in favor of calling exe 2023-10-05 13:35:12 -04:00
dyoder 983952ee99 move check values to left side of comparison 2023-10-05 13:33:28 -04:00
dyoder ab2a1821d5 remove alias in favor of cmdlet name 2023-10-05 13:32:26 -04:00
dyoder df7f635e26 remove alias in favor of cmdlet name 2023-10-05 13:19:25 -04:00
dyoder c628f60289 remove alias in favor of cmdlet name 2023-10-05 13:18:40 -04:00
dyoder 5f4b4565de major change to override Write-Error func 2023-10-05 13:17:18 -04:00
dyoder bbb4884818 major update to override Write-Output func 2023-10-05 13:11:38 -04:00
dyoder 31a06a9206 changed function GetTempPath to Get-TempPath 2023-10-05 12:47:08 -04:00
dyoder 32b33cee34 update with guards,
improved output,
head
2023-10-04 16:38:02 -04:00
dyoder c50ce1097f minor improvements 2023-10-03 16:23:51 -04:00
dyoder de68ede13a initial commit 2023-09-29 16:49:05 -04:00
dyoder 764eaac9b2 added output messages 2023-09-25 20:54:29 -04:00
dyoder 30cd1301ee initial commit 2023-09-25 20:48:59 -04:00
dyoder f7e73061af commended out a func ref 2023-09-18 16:19:21 -04:00
dyoder d93a43d260 initial commit 2023-09-15 16:48:07 -04:00
dyoder 618cf7a7b7 source tools directly 2023-09-15 16:47:50 -04:00
dyoder a3278beb71 minor changes - may be used more later 2023-09-15 16:46:10 -04:00
dyoder 9d7c5d59ba minor fixes in log message wording 2023-09-15 13:20:55 -04:00
dyoder 99b87f468b use variable vals, check existing vals first 2023-09-15 13:15:23 -04:00
dyoder 2bc320480f see previous comment :-( 2023-09-14 15:01:00 -04:00
dyoder 60ffce3812 forgot to update comment to reflect last change 2023-09-14 15:00:27 -04:00
dyoder bb45975284 increase TDR value to 64 seconds 2023-09-14 15:00:03 -04:00
dyoder c275073c45 add some output 2023-09-14 13:06:47 -04:00
dyoder 103122c1e0 initial commit 2023-09-14 13:02:35 -04:00
dyoder ebbee7c0d0 move script out of Atera and host it here 2023-09-14 11:48:43 -04:00
dyoder 9816d15165 remove unnecessary prints to console 2023-09-11 12:46:43 -04:00
dyoder 0360329312 added function Restart-EndpointOnUptime 2023-09-07 10:59:55 -04:00
dyoder 6a6c3fc9cf update to 3.5.2 2023-08-28 13:58:33 -04:00
dyoder 21cafbb931 handle errors accessing registry in Uninstall-MSI 2023-08-23 15:10:45 -04:00
dyoder 01a04a1127 added check for Get-GUIDFromMSIUninstallString 2023-08-23 15:01:36 -04:00
dyoder c1c7681370 cleared some warnings in the IDE 2023-08-23 14:50:47 -04:00
dyoder 70a7ddb75d overhaul Uninstall-MSI for pipeline input 2023-08-23 13:56:04 -04:00
dyoder b52984ff32 moved into Tools.ps1 2023-08-22 16:32:38 -04:00
dyoder c32e74002b update uninstall procedure to use new function 2023-08-22 16:31:56 -04:00
dyoder 1a731c2b49 update comments for uninstall procedure 2023-08-22 16:31:34 -04:00
dyoder c2f5c06996 match on wildcard for uninstall 2023-08-22 16:31:19 -04:00
dyoder cc97f9e592 pipe agents into Uninstall-MSI for future 2023-08-22 16:25:39 -04:00
dyoder f21e5ff768 accept from pipe in Uninstall-MSI 2023-08-22 16:24:39 -04:00
dyoder 3929a896ed uninstall existing LF agents before installing 2023-08-22 16:23:39 -04:00
dyoder f6ebc2f0fa added new function Get-GUIDFromMSIUninstallString
added new function Uninstall-MSI
2023-08-22 16:09:13 -04:00
dyoder 3f36b91570 don't exit script if already installed 2023-08-22 11:53:57 -04:00
dyoder 20fb02e026 make sure the agent can be started after install 2023-08-22 11:40:27 -04:00
dyoder 9747a38fab remove a test 2023-08-22 11:39:33 -04:00
dyoder f09b44d15e exit installer if agent is installed 2023-08-22 11:14:26 -04:00
dyoder 3f6346d02c downgrade to 2.1.10 2023-08-22 11:08:54 -04:00
dyoder 794fddda73 update to 3.0.19 2023-08-21 17:05:27 -04:00
dyoder e0e254d799 remove unused code
make text easier to identify in output
2023-08-14 12:24:27 -04:00
dyoder 6a47fb6a64 fixed typo 2023-08-11 11:27:30 -04:00
dyoder 6d77d8fab4 cleanup comments 2023-08-11 11:25:57 -04:00
dyoder e3db5cb6e7 initial commit 2023-08-11 11:24:45 -04:00
dyoder aa48a738fa remove unnecessary sleep 2023-07-28 11:30:50 -04:00
dyoder 80979fc137 close all office apps before uninstalling 2023-07-28 11:30:00 -04:00
dyoder 9644829b9e eliminate alias 2023-07-28 09:28:37 -04:00
dyoder d4e716f15b initial commit 2023-07-28 09:27:38 -04:00
dyoder 8440c1ea05 update build to 10.0.19045 2023-07-26 14:00:52 -04:00
dyoder 19d2f40526 update to 22H2 2023-07-25 17:25:30 -04:00
dyoder e0b39c591b initial commit 2023-07-13 11:27:01 -04:00
dyoder 9bb5239e61 add install params 2023-07-13 11:26:48 -04:00
dyoder fc0b916a38 update to version 3.5.1 2023-06-27 21:32:07 -04:00
dyoder e7b9b3b9dc update to version 7.8.7.58 2023-06-02 10:47:44 -04:00
dyoder 6204176ec5 remove double hash in comment lines 2023-06-02 09:59:01 -04:00
dyoder bf20a24cdb updated to version 2.1.6 2023-05-31 11:55:23 -04:00
dyoder 231da6ace4 initial commit 2023-05-23 16:47:59 -04:00
dyoder 4a7df59b1c update to 7.8.6.13 2023-05-23 16:47:10 -04:00
dyoder d8c144ff76 initial commit 2023-04-26 12:22:22 -04:00
dyoder c2ebb5cfeb initial commit 2023-04-26 12:22:13 -04:00
dyoder 31c8a2719b update to version 7.8.5.12 2023-04-12 10:52:19 -04:00
dyoder 2cb74b37de update to version 7.8.4.135 2023-04-07 15:54:50 -04:00
dyoder 223d8ea488 updated to version 7.8.4.129 2023-03-27 10:52:22 -04:00
dyoder 9358d85d27 initial commit 2023-03-20 21:47:38 -04:00
dyoder 0d1216a5cf updated installer links to v7.8.3 2023-03-20 21:47:19 -04:00
dyoder c9502bf051 not sure what changed here 2023-03-14 21:58:20 -04:00
dyoder 7ce453114b renamed script 2023-03-14 21:48:01 -04:00
dyoder 5207d8719f update agent version 2023-02-09 10:14:26 -05:00
dyoder 522f0abda3 initial commit 2023-01-30 14:30:27 -05:00
dyoder 8ace59d30a place app tracking above loop 2023-01-18 19:38:18 -05:00
dyoder 1c4aa415e1 place no product matching statement outside loop 2023-01-18 19:37:06 -05:00
dyoder 183bafcba1 remove the uninstall procedure until it's fixed 2023-01-18 19:32:05 -05:00
dyoder aa6c5f0179 change app tracking from int to bool 2023-01-18 19:22:52 -05:00
dyoder 3ada68d37f fix app matching for uninstall 2023-01-18 19:14:05 -05:00
dyoder 933105f0bf log msg if no product was uninstalled 2023-01-18 19:13:41 -05:00
dyoder 0fa22b397c initial commit 2023-01-18 12:56:50 -05:00
dyoder fab9912fc8 added removal of Autodesk installers 2023-01-16 12:55:48 -05:00
dyoder 35e3bcd03d initial commit 2023-01-13 08:36:36 -05:00
dyoder ea77a6f7ee update uninstall method 2023-01-13 08:34:55 -05:00
dyoder 35dd913537 minor fixes for userprofile cleaning 2023-01-09 16:01:01 -05:00
dyoder e4bffa2caa Merge branch 'master' of https://dev.emberkom.com/emberkom/management-scripts 2023-01-06 13:22:23 -05:00
dyoder 338b2915cf get rid of testing lines 2023-01-06 13:22:18 -05:00
dyoder 25ef1c9b2a get rid of testing lines 2023-01-04 11:32:56 -05:00
dyoder 7094ff6f8e added new function Get-AbsoluteURI 2023-01-04 10:42:37 -05:00
dyoder fe2e50e456 added new function Get-AbsoluteURI 2023-01-04 10:42:02 -05:00
dyoder e39606cd1b update download urls 2023-01-04 10:41:35 -05:00
dyoder 6bfc18a15f minor fix 2023-01-04 10:41:10 -05:00
dyoder 678b808e64 don't add account again 2022-12-16 12:15:18 -05:00
dyoder 61990346d0 removed wait param 2022-12-15 14:23:26 -05:00
dyoder 74559046c0 change installation to manual 2022-12-15 14:18:31 -05:00
dyoder 88b721a7f9 move import-module inside job scriptblock 2022-12-15 14:13:09 -05:00
dyoder 1cc289c119 install using ps job 2022-12-15 14:03:05 -05:00
dyoder 1c5d7e26d6 wait for uninstall to complete before continuing 2022-12-15 13:43:46 -05:00
dyoder a3825836f7 initial commit 2022-12-15 13:35:21 -05:00
dyoder fd6ca90bb0 cleanup comments 2022-12-15 13:33:26 -05:00
dyoder e138e811ce wait for install to complete before continuing 2022-12-15 13:31:09 -05:00
dyoder c804483441 uppercase function keyword
add function Beautify-Bytes
2022-12-15 12:03:47 -05:00
dyoder 6fc62ee5b2 update urls to version 7.8.1.350 2022-12-15 12:02:53 -05:00
dyoder bc33eedf19 remove AMD for enabling gpu scheduling 2022-11-28 14:30:29 -05:00
dyoder 5f61ae4758 itunes section isn't working, commented out 2022-11-28 14:13:12 -05:00
dyoder 999ec1a4a7 minor fix 2022-11-25 11:34:48 -05:00
dyoder 9d4739beba fix? 2022-11-23 15:59:33 -05:00
dyoder 855f186aba check that reg property exists 2022-11-23 15:52:49 -05:00
dyoder 10bd04c73d added function Test-RegistryProperty 2022-11-23 15:52:30 -05:00
dyoder cdab3ef78b initial commit 2022-11-23 14:17:38 -05:00
dyoder e900b65cc5 added new removal for outlook backup files 2022-11-22 13:20:18 -05:00
dyoder 64d6b36bd0 added function EnumUserProfiles 2022-11-19 14:38:02 -05:00
dyoder 5a08df43f0 overhaul 2022-11-19 14:37:35 -05:00
196 changed files with 7049 additions and 977 deletions
+21
View File
@@ -0,0 +1,21 @@
# Repository Guidance
## RMM script execution model
Scripts in this repository are generally executed by a small caller script in the RMM. The caller first downloads and dot-sources `Tools.ps1` from the public repository URL:
```powershell
. $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1')))
```
The caller then invokes `Run-Script`, passing the repository script's filename without the `.ps1` extension:
```powershell
Run-Script -LivePSScript Create-BatteryReport
```
For a live PowerShell script without `-Arguments`, `Run-Script` downloads the target script and dot-sources it. This keeps the core script logic in version control, and changes pushed to the repository take effect in production without updating each RMM caller.
When a repository script needs RMM-provided input, define the variable in the RMM caller before calling `Run-Script`. Because the target is dot-sourced into the caller's scope, it can read that variable directly.
When changing `Run-Script` or scripts invoked through it, preserve this shared-scope behavior unless the change explicitly includes migrating all affected RMM callers.
@@ -0,0 +1,79 @@
# $CleanupPath must be set by RMM
# $DaysWithNoModification must be set by RMM
# Exit if the source path does not exist
If ( !(Test-Path $CleanupPath) ) { Write-Output "The specified path does not exist: ""${CleanupPath}""" ; Return }
# Running total of the size of all directories and files
$TotalSize = 0
# Running total of the number of all identified directories
$TotalDirectories = 0
# Running total of the number of all identified files
$TotalFiles = 0
$StartDate = Get-Date
$StartTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output '-------------------------------------------------------------------------------'
Write-Output "Started at : ${StartTime}"
Write-Output "Run on : ${Env:COMPUTERNAME}"
Write-Output "Source : ${CleanupPath}"
If ( $DaysWithNoModification -gt 0 ) { Write-Output "Modified : ${DaysWithNoModification} day(s) ago" }
Write-Output '-------------------------------------------------------------------------------'
# Identify all files and folders that match the criteria for what we're looking for
$TEMPORARY_REVIT_OBJECTS = Get-ChildItem -Path $CleanupPath -Recurse | Where-Object {
(
# Revit model backup folder
$_.Name -match '_backup$' -or
# Revit backup models
$_.Name -match '\.\d\d\d\d\.rvt$'
# Files and folders not recently modified
) -and ($_.LastWriteTime -lt $StartDate.AddDays(-$DaysWithNoModification))
}
# Loop thru each path to get its size and
Foreach ( $obj in $TEMPORARY_REVIT_OBJECTS ) {
# Skip the object if it no longer exists or is otherwise inaccessible
If ( !(Test-Path $obj.FullName) ) { Continue } Else { $item = $obj.FullName }
Switch ( $obj.PSIsContainer ) {
# Get the size of the directory and update counter
$true {
$size = (Get-ChildItem -Path $item -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
$TotalDirectories++
}
# Get the size of the file and update counter
$false {
$size = (Get-Item $item | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
$TotalFiles++
}
}
Write-Output $item
$TotalSize += $size
}
# Record stats
$EndTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$Duration = "{0:g}" -f (New-TimeSpan -Start $StartTime -End $EndTime)
Switch ( $TotalSize ) {
{ $_ -ge 1TB } { $GrandTotal = "{0}TB" -f ([Math]::Round(($TotalSize / 1TB),2)) ; break }
{ $_ -ge 1GB } { $GrandTotal = "{0}GB" -f ([Math]::Round(($TotalSize / 1GB),2)) ; break }
{ $_ -ge 1MB } { $GrandTotal = "{0}MB" -f ([Math]::Round(($TotalSize / 1MB),2)) ; break }
{ $_ -ge 1KB } { $GrandTotal = "{0}KB" -f ([Math]::Round(($TotalSize / 1KB),2)) ; break }
{ $_ -lt 1KB } { $GrandTotal = "${TotalSize} bytes" ; break }
}
Write-Output '-------------------------------------------------------------------------------'
Write-Output "Finished at: ${EndTime}"
Write-Output "Duration : ${Duration}"
Write-Output "Directories: ${TotalDirectories}"
Write-Output "Files : ${TotalFiles}"
Write-Output "Total size : ${GrandTotal}"
Write-Output '-------------------------------------------------------------------------------'
+5 -5
View File
@@ -19,18 +19,18 @@ Foreach ( $path in $CURRENT_PATH.Split(";") )
}
}
LogMsg "Removed Paths:`n`r${REMOVE_PATH}"
Write-Output "Removed Paths:`n`r${REMOVE_PATH}"
Try { Set-ItemProperty -Path $REG_ENVVAR -Name Path -Value $NEW_PATH }
Catch
{
LogErr $_.Exception.Message
LogErr "Failed to cleanup system path, using original values"
Write-Error $_.Exception.Message
Write-Error "Failed to cleanup system path, using original values"
Try { Set-ItemProperty -Path $REG_ENVVAR -Name Path -Value $CURRENT_PATH }
Catch
{
LogErr $_.Exception.Message
LogErr "This is not good... !! DO NOT RESTART UNTIL SYSTEM PATH IS FIXED !!"
Write-Error $_.Exception.Message
Write-Error "This is not good... !! DO NOT RESTART UNTIL SYSTEM PATH IS FIXED !!"
Exit
}
}
+256 -102
View File
@@ -1,121 +1,275 @@
. $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1')))
# When deleting temp files en masse, only delete files/folders older than the number of days specified here
$OlderThan = 7
$TimeDelta = New-TimeSpan -Days $OlderThan
# Only run this section if we have administrative privileges
If ( IsAdmin )
{
LogMsg "Running with administrative privileges"
# Remove all *.evtx files from system temporary files location
$EventLogs = Get-ChildItem -Path "${Env:SystemRoot}\TEMP\*" -File -Filter *.evtx
If ( $EventLogs ) {
Write-Output "Deleting all event logs from ${Env:SystemRoot}\TEMP"
ForEach ( $EventLog in $EventLogs ) { Remove-Item $EventLog -Force -ErrorAction SilentlyContinue }
}
# Remove all *.evtx files from $SysTemp
$EventLogs = Get-ChildItem -Path "${Env:SystemRoot}\TEMP\*" -File -Filter *.evtx
If ( $EventLogs )
# Remove misc logs and error reports
$MiscLogs = Get-ChildItem -Path "${Env:SystemRoot}\TEMP\*" -File -Include "${Env:COMPUTERNAME}-*.log", "AppxErrorReport_*.txt"
If ( $MiscLogs ) {
Write-Output "Deleting misc logs from ${Env:SystemRoot}\TEMP"
ForEach ( $MiscLog in $MiscLogs ) { Remove-Item $MiscLog -Force -ErrorAction SilentlyContinue }
}
# Remove archived CBS logs
$CBSLogs = Get-ChildItem -Path "${Env:SystemRoot}\Logs\CBS\*" -File -Include "CbsPersist_*.log", "CbsPersist_*.cab"
If ( $CBSLogs ) {
$TotalBytes = 0
ForEach ( $CBSLog in $CBSLogs) { $TotalBytes += $CBSLog.Length }
$TotalSize = [math]::Round(($TotalBytes / 1MB),2)
If ( $TotalSize -ge 200 ) {
Write-Output "Total CBS log archive size: ${TotalSize}MB"
Control-Service -Stop 'TrustedInstaller'
Write-Output "Deleting primary CBS.log file"
Remove-Item "${Env:SystemRoot}\Logs\CBS\CBS.log" -Force -ErrorAction SilentlyContinue
Control-Service -Start 'TrustedInstaller'
}
Write-Output "Deleting archived CBS logs from ${Env:SystemRoot}\Logs\CBS"
ForEach ( $CBSLog in $CBSLogs ) { Remove-Item $CBSLog -Force -ErrorAction SilentlyContinue }
}
# Remove all system temp files older than 7 days
$OldTempFiles = Get-ChildItem -Path "${Env:SystemRoot}\TEMP" | Where-Object { $_.LastWriteTime -lt ((Get-Date) - $TimeDelta) }
If ( $OldTempFiles ) {
Write-Output "Deleting all temp files not modified in ${OlderThan} day(s) from ${Env:SystemRoot}\TEMP"
ForEach ( $OldTempFile in $OldTempFiles ) { Remove-Item $OldTempFile.FullName -Force -Recurse -ErrorAction SilentlyContinue }
}
# Remove all old files from the Recycle Bin
$UserRecycleBins = Get-ChildItem -Path "${Env:SystemDrive}\`$Recycle.Bin" -Directory -ErrorAction SilentlyContinue
If ( ($UserRecycleBins | Measure-Object).Count -gt 0 ) {
Write-Output "Deleting all recycle bin items older than ${OlderThan} days from:"
ForEach ( $userRecycleBin in $UserRecycleBins ) {
$userRecycleBinFullName = $userRecycleBin.FullName
Write-Output " - $userRecycleBinFullName"
$items = Get-ChildItem -Path $userRecycleBin.FullName -Recurse -ErrorAction SilentlyContinue
ForEach ( $item in $items ) {
If ( $item.LastWriteTime -lt $TimeDelta ) {
Try { Remove-Item -Path $item.FullName -Force -ErrorAction Continue }
Catch { Write-Error $_.Exception.Message }
}
}
}
}
# Turn off system hibernation if it's in use
If ( Test-Path "${Env:SystemDrive}\hiberfil.sys" ) {
Write-Output "Turning off system hibernation"
Try { Start-Process "powercfg" -ArgumentList "-h off" -Wait }
Catch { Write-Error $_.Exception.Message }
}
# Remove PDCRevocation dump files
$PDCRevocationDumpFiles = Get-ChildItem -Path "${Env:SystemRoot}\LiveKernelReports\*" -File -Include "PDCRevocation-*.dmp"
If ( $PDCRevocationDumpFiles ) {
Write-Output "Deleting PDCRevocation dump files from ${Env:SystemRoot}\LiveKernelReports"
ForEach ( $PDCRevocationDump in $PDCRevocationDumpFiles ) { Remove-Item $PDCRevocationDump.FullName -Force -ErrorAction SilentlyContinue }
}
# Remove Genetec application crash dumps
If ( Test-Path "${Env:ProgramData}\Genetec\Dumps" ) {
$GenetecCrashDumps = Get-ChildItem -Path "${Env:ProgramData}\Genetec\Dumps"
If ( $GenetecCrashDumps )
{
LogMsg "Deleting all event logs from ${Env:SystemRoot}\TEMP"
ForEach ( $EventLog in $EventLogs ) { Remove-Item $EventLog -Force -ErrorAction SilentlyContinue }
Write-Output "Deleting Genetec application crash dumps"
Try { ForEach ( $dump in $GenetecCrashDumps ) { Remove-Item $dump.FullName -Force -Recurse -ErrorAction SilentlyContinue } }
Catch { Write-Error $_.Exception.Message }
}
}
# Defragment Windows Elastic Search database
$ESDB = "${Env:ProgramData}\Microsoft\Search\Data\Applications\Windows\Windows.edb"
If ( Test-Path $ESDB ) {
Try {
Write-Output "Found Windows Elastic Search database: ""${ESDB}"""
Write-Output " - Disabling Windows Search service"
Start-Process "sc.exe" -ArgumentList "config wsearch start= disabled" -Wait
Write-Output " - Stopping Windows Search Service"
Start-Process "net.exe" -ArgumentList "stop wsearch" -Wait
Write-Output " - Defragmenting database file"
Start-Process "esentutl.exe" -ArgumentList "/d ""${ESDB}""" -Wait
Write-Output " - Enabling Windows Search Service"
Start-Process "sc.exe" -ArgumentList "config wsearch start= delayed-auto" -Wait
Write-Output " - Starting Windows Search Service"
Start-Process "net.exe" -ArgumentList "start wsearch" -Wait
}
Catch { Write-Error $_.Exception.Message }
}
# Remove any Autodesk installers at their default location
$ADSKInst = "${Env:SystemDrive}\Autodesk"
If ( Test-Path $ADSKInst ) {
$foldersToDelete = Get-ChildItem -Path $ADSKInst -ErrorAction SilentlyContinue | Where-Object { ($_.PSIsContainer) -and ($_.LastWriteTime -lt ((Get-Date) - $TimeDelta)) -and ( $_.Name -ne "WI") }
ForEach ( $f in $foldersToDelete ) {
$path = $f.FullName
Write-Output "Deleting Autodesk installer directory: ""${path}"""
Try { Remove-Item $f.FullName -Force -Recurse -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
}
# Delete all but most recent shadow copy of system drive
$SystemVolume = Get-CimInstance -ClassName Win32_Volume -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "${Env:SystemDrive}\" }
$ShadowCopies = Get-CimInstance -ClassName Win32_ShadowCopy -ErrorAction SilentlyContinue | Where-Object { $_.VolumeName -like $SystemVolume.DeviceID }
If ( $ShadowCopies.Count -gt 1 ) {
Write-Output "Deleting all of the oldest shadow copies of ${Env:SystemDrive}"
$ShadowsToDelete = $ShadowCopies.Count - 1
While ( $ShadowsToDelete -gt 0 ) {
vssadmin delete shadows /For=$Env:SystemDrive /Oldest /Quiet
$ShadowsToDelete = $ShadowsToDelete - 1
}
}
# Delete old temp directories from Chocolatey cache
$ChocolateyCache = "${Env:WinDir}\Temp\chocolatey"
$ChocolateyTempDirs = Get-ChildItem -Path $ChocolateyCache -Directory -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt ((Get-Date) - $TimeDelta) }
If ( $ChocolateyTempDirs ) {
Write-Output "Deleting all temp directories not modified in ${OlderThan} day(s) from ""${ChocolateyCache}"""
ForEach ( $d in $ChocolateyTempDirs ) {
Try { Remove-Item $d.FullName -Force -Recurse -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
}
# Remove files from user profile directories
ForEach ( $userProfile in $(Get-UserProfiles) ) {
# Remove all user temp files older than 7 days
$OldTempFiles = Get-ChildItem -Path "${userProfile}\AppData\Local\Temp" -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt ((Get-Date) - $TimeDelta) }
If ( $OldTempFiles ) {
Write-Output "Deleting all temp files not modified in ${OlderThan} day(s) from ""${userProfile}\AppData\Local\Temp"""
ForEach ( $OldTempFile in $OldTempFiles ) { Remove-Item $OldTempFile.FullName -Force -Recurse -ErrorAction SilentlyContinue }
}
# Remove misc logs and error reports
$MiscLogs = Get-ChildItem -Path "${Env:SystemRoot}\TEMP\*" -File -Include "${Env:COMPUTERNAME}-*.log", "AppxErrorReport_*.txt"
If ( $MiscLogs )
{
LogMsg "Deleting misc logs from ${Env:SystemRoot}\TEMP"
ForEach ( $MiscLog in $MiscLogs ) { Remove-Item $MiscLog -Force -ErrorAction SilentlyContinue }
# Remove AutoCAD temp files
If (-not (IsRunning -Name "acad") ) {
$AutoCADTempFiles = Get-ChildItem -Path "${userProfile}\AppData\Local\Temp\*" -File -Include '*.ac$' -ErrorAction SilentlyContinue
If ( $AutoCADTempFiles ) {
Write-Output "Deleting AutoCAD temp files"
ForEach ( $tempfile in $AutoCADTempFiles ) {
Try { Remove-Item $tempfile.FullName -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
}
}
# Apple iTunes data
# If (-not (IsRunning -Name 'itunes') ) {
# # Remove Apple software updates downloaded from iTunes
# $IPSWBasePath = "${userProfile}\AppData\Roaming\Apple Computer\iTunes"
# $IPSW = @()
# # iPhone update files
# ForEach ( $updateFile in $(Get-ChildItem -Path "${IPSWBasePath}\iPhone Software Updates\*" -File -Include '*.ipsw' -ErrorAction SilentlyContinue) ) {
# $IPSW += ,@($updateFile.FullName)
# }
# # iPad update files
# ForEach ( $updateFile in $(Get-ChildItem -Path "${IPSWBasePath}\iPad Software Updates\*" -File -Include '*.ipsw' -ErrorAction SilentlyContinue) ) {
# $IPSW += ,@($updateFile.FullName)
# }
# # iPod update files
# ForEach ( $updateFile in $(Get-ChildItem -Path "${IPSWBasePath}\iPod Software Updates\*" -File -Include '*.ipsw' -ErrorAction SilentlyContinue) ) {
# $IPSW += ,@($updateFile.FullName)
# }
# # Remove Apple mobile applications downloaded from iTunes
# $IPA = @()
# ForEach ( $mobileApp in $(Get-ChildItem -Path "${userProfile}\Music\iTunes\iTunes Media\Mobile Applications\*" -File -Include '*.ipa' -ErrorAction SilentlyContinue) ) {
# $IPA += ,@($updateFile.FullName)
# }
# If ( $IPSW ) {
# Write-Output "Found Apple software update files"
# ForEach ( $path in $IPSW ) {
# Write-Output " - Deleting: ""${path}"""
# Try { Remove-Item -Path $path -Force -ErrorAction SilentlyContinue }
# Catch { Write-Error $_.Exception.Message }
# }
# }
# If ( $IPA ) {
# Write-Output "Found Apple mobile application files"
# ForEach ( $path in $IPA ) {
# Write-Output " - Deleting: ""${path}"""
# Try { Remove-Item -Path $path -Force -ErrorAction SilentlyContinue }
# Catch { Write-Error $_.Exception.Message }
# }
# }
# }
# Remove old downloaded application files
$OldDownloadedApps = (Get-ChildItem -Path "${userProfile}\Downloads\*" -File -Filter '*.exe' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt ((Get-Date) - $TimeDelta) })
If ( $OldDownloadedApps ) {
Write-Output "Deleting old downloaded app files"
ForEach ( $oldDownloadedApp in $OldDownloadedApps ) {
$path = $oldDownloadedApp.FullName
Write-Output " - Deleting: ""${path}"""
Try { Remove-Item -Path $path -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
}
# Remove archived CBS logs
$CBSLogs = Get-ChildItem -Path "${Env:SystemRoot}\Logs\CBS\*" -File -Include "CbsPersist_*.log", "CbsPersist_*.cab"
If ( $CBSLogs )
{
$TotalBytes = 0
ForEach ( $CBSLog in $CBSLogs) { $TotalBytes += $CBSLog.Length }
$TotalSize = $TotalBytes / 1MB
If ( $TotalSize -ge 200 ) {
LogMsg "Total CBS log archive size: ${TotalSize}MB"
Control-Service -Stop 'TrustedInstaller'
LogMsg "Deleting primary CBS.log file"
Remove-Item "${Env:SystemRoot}\Logs\CBS\CBS.log" -Force -ErrorAction SilentlyContinue }
Control-Service -Start 'TrustedInstaller'
# Remove duplicate downloads
# Separate the possible duplicates from the rest
$PossibleDuplicates = @()
$NotDuplicates = @()
ForEach ( $file in (Get-ChildItem -Path "${userProfile}\Downloads\*" -File -ErrorAction SilentlyContinue) ) {
If ( $file.BaseName -match '\s\(\d*\)$' ) {
$PossibleDuplicates += ,@($file)
} Else {
$NotDuplicates += ,@($file)
}
LogMsg "Deleting archived CBS logs from ${Env:SystemRoot}\Logs\CBS"
ForEach ( $CBSLog in $CBSLogs ) { Remove-Item $CBSLog -Force -ErrorAction SilentlyContinue }
}
# Loop through all files
ForEach ( $pd in $PossibleDuplicates ) {
ForEach ( $nd in $NotDuplicates ) {
}
# Remove all system temp files older than 7 days
$OldTempFiles = Get-ChildItem -Path "${Env:SystemRoot}\TEMP" | Where { $_.LastWriteTime -lt ((Get-Date) - $TimeDelta) }
If ( $OldTempFiles )
{
LogMsg "Deleting all temp files not modified in ${OlderThan} day(s) from ${Env:SystemRoot}\TEMP"
ForEach ( $OldTempFile in $OldTempFiles ) { Remove-Item $OldTempFile -Force -Recurse -ErrorAction SilentlyContinue }
}
# Turn off system hibernation if it's in use
If ( Test-Path "${Env:SystemDrive}\hiberfil.sys" )
{
LogMsg "Turning off system hibernation"
Try { Start-Process "powercfg" -ArgumentList "-h off" -Wait }
Catch { LogErr $_.Exception.Message }
}
# Remove PDCRevocation dump files
$PDCRevocationDumpFiles = Get-ChildItem -Path "${Env:SystemRoot}\LiveKernelReports\*" -File -Include "PDCRevocation-*.dmp"
If ( $PDCRevocationDumpFiles ) {
LogMsg "Deleting PDCRevocation dump files from ${Env:SystemRoot}\LiveKernelReports"
ForEach ( $PDCRevocationDump in $PDCRevocationDumpFiles ) { Remove-Item $PDCRevocationDump -Force -ErrorAction SilentlyContinue }
}
# Remove Genetec application crash dumps
If ( Test-Path "${Env:ProgramData}\Genetec\Dumps" ) {
$GenetecCrashDumps = Get-ChildItem -Path "${Env:ProgramData}\Genetec\Dumps"
If ( $GenetecCrashDumps )
{
LogMsg "Deleting Genetec application crash dumps"
Try { ForEach ( $dump in $GenetecCrashDumps ) { Remove-Item $dump.FullName -Force -Recurse -ErrorAction SilentlyContinue } }
Catch { LogErr $_.Exception.Message }
# Match the first part of $pd to $nd
If ( [regex]::Match($pd.BaseName,'^(.*)(\s\(\d*\))$').Groups[1].Value -eq $nd.BaseName ) {
# Check for equal file size
If ( $(Get-Item -Path $pd.FullName).Length -eq $(Get-Item -Path $nd.FullName).Length ) {
# Make sure the file still exists before writing any log messages
$ToDelete = $pd.FullName
If ( Test-Path $ToDelete ) {
# Delete the duplicate file
Write-Output "Deleting duplicate download: ""${ToDelete}"""
Try { Remove-Item -Path $ToDelete -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
}
}
}
}
# Remove AutoCAD temp files
If (-not (IsRunning -Name "acad") ) {
$AutoCADTempFiles = Get-ChildItem -Path "${Env:SystemDrive}\Users" | Where-Object { $_.PSIsContainer -eq $true } | ForEach-Object { Get-ChildItem -Path $($_.FullName + "\AppData\Local\Temp\*") -File -Include '*.ac$' -ErrorAction SilentlyContinue }
If ( $AutoCADTempFiles ) {
LogMsg "Deleting AutoCAD temp files for all users"
ForEach ( $tempfile in $AutoCADTempFiles ) { Remove-Item $tempfile -Force -ErrorAction SilentlyContinue }
# Remove old Outlook backup files
$OldOutlookBackupFiles = Get-ChildItem -Path "${userProfile}\AppData\Local\Microsoft\Outlook\*" -File -Filter '*.bak' -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt ((Get-Date) - $TimeDelta) }
If ( $OldOutlookBackupFiles ) {
Write-Output "Found Outlook backup files older than ${OlderThan} days:"
ForEach ( $file in $OldOutlookBackupFiles ) {
$path = $file.FullName
Write-Output "Deleting: ""${path}"""
Try { Remove-Item -Path $path -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
}
# Silently run cleanmgr.exe with default settings
LogMsg "Running cleanmgr.exe with default settings"
Try { Start-Process "cleanmgr.exe" -ArgumentList "/VERYLOWDISK" }
Catch { LogErr $_.Exception.Message }
}
# Only run this section if we don't have administrative privileges
Else
{
LogMsg "Running without administrative privileges"
# Remove all system temp files older than 7 days
$OldTempFiles = Get-ChildItem -Path $Env:TEMP | Where { $_.LastWriteTime -lt ((Get-Date) - $TimeDelta) }
If ( $OldTempFiles )
{
LogMsg "Deleting all temp files not modified in ${OlderThan} day(s) from ${Env:TEMP}"
ForEach ( $OldTempFile in $OldTempFiles ) { Remove-Item $OldTempFile -Force -Recurse -ErrorAction SilentlyContinue }
}
# Remove AutoCAD temp files
If (-not (IsRunning -Name "acad") ) {
$AutoCADTempFiles = Get-ChildItem -Path "${Env:TEMP}\*" -File -Include '*.ac$' -ErrorAction SilentlyContinue
If ( $AutoCADTempFiles ) {
LogMsg "Deleting AutoCAD temp files"
ForEach ( $tempfile in $AutoCADTempFiles ) { Remove-Item $tempfile -Force -ErrorAction SilentlyContinue }
}
}
# Silently run cleanmgr.exe with default settings
LogMsg "Running cleanmgr.exe with default settings"
Try { Start-Process "cleanmgr.exe" -ArgumentList "/VERYLOWDISK" }
Catch { LogErr $_.Exception.Message }
}
# Silently run cleanmgr.exe with default settings
# TODO: This does not run silently - commenting out for now!
#Try {
# Write-Output "Running cleanmgr.exe with default settings for very low disk space"
# Start-Process "cleanmgr.exe" -ArgumentList "/VERYLOWDISK"
# Write-Output " - Finished"
#}
#Catch { Write-Error $_.Exception.Message }
+15
View File
@@ -0,0 +1,15 @@
$LogsToEnable = @(
''
)
$LogsToDisable = @(
''
)
ForEach ( $log in $LogsToEnable ) {
Enable-Log -Name $log
}
ForEach ( $log in $LogsToDisable) {
Disable-Log -Name $log
}
+79
View File
@@ -0,0 +1,79 @@
# Exit this script if the current device doesn't have a built-in battery
If ( $null -eq (Get-CimInstance -ClassName Win32_Battery -ErrorAction SilentlyContinue) ) { Microsoft.Powershell.Utility\Write-Output "This endpoint does not have a battery" ; Exit }
# Define file paths
$Today = Get-LongDate
$ReportPathNoExt = "${OutputDirectory}\batteryreport_${Env:COMPUTERNAME}_${Today}"
$HtmlReportPath = $ReportPathNoExt + '.html'
$XmlReportPath = $ReportPathNoExt + '.xml'
# Delete reports if they already exist
If ( Test-Path $XmlReportPath ) { Remove-Item -Path $XmlReportPath -Force }
If ( Test-Path $HtmlReportPath ) { Remove-Item -Path $HtmlReportPath -Force }
# Generate the XML report
Write-Output "Generating XML battery report: ${XmlReportPath}"
Try {
$XmlProcess = Start-Process "powercfg.exe" `
-ArgumentList "/batteryreport /output ""${XmlReportPath}"" /xml" `
-Wait `
-PassThru `
-ErrorAction Stop
}
Catch {
Write-Error $_.Exception.Message
Exit 1
}
# Make sure powercfg succeeded and actually produced the report
If ( $XmlProcess.ExitCode -ne 0 ) {
Write-Error "powercfg failed to generate the XML battery report with exit code $($XmlProcess.ExitCode)"
Exit 1
}
If ( !(Test-Path -LiteralPath $XmlReportPath -PathType Leaf) ) {
Write-Error "The XML battery report was not created: ${XmlReportPath}"
Exit 1
}
# Validate the generated XML and confirm that it contains battery records
Try {
[xml]$GeneratedBatteryReport = Get-Content `
-LiteralPath $XmlReportPath `
-Raw `
-ErrorAction Stop
}
Catch {
Write-Error "The generated battery report is not valid XML: $($_.Exception.Message)"
Exit 1
}
$GeneratedBatteries = @(
$GeneratedBatteryReport.BatteryReport.Batteries.Battery
)
If ( $GeneratedBatteries.Count -eq 0 ) {
Write-Error "The generated battery report contains no battery records"
Exit 1
}
# Convert XML report to HTML
Write-Output "Generating HTML battery report: ${HtmlReportPath}"
Try {
$HtmlProcess = Start-Process "powercfg.exe" `
-ArgumentList "/batteryreport /transformxml ""${XmlReportPath}"" /output ""${HtmlReportPath}""" `
-Wait `
-PassThru `
-ErrorAction Stop
}
Catch {
Write-Error $_.Exception.Message
Exit 1
}
If ( $HtmlProcess.ExitCode -ne 0 -or !(Test-Path -LiteralPath $HtmlReportPath -PathType Leaf) ) {
Write-Error "Failed to create the HTML battery report"
Exit 1
}
+21
View File
@@ -0,0 +1,21 @@
# Eval the alert data
If ( -not $AlertData ) { Write-Error "No alert data provided" ; Return } Else { Write-Output $AlertData ; Return }
## Create ticket
#New-RMMTicket -Subject "App Crash on ${Env:COMPUTERNAME}" -InitialIssue "Application crash detected. Reason for crash indicates a Windows component is at fault."
#
## Update the ticket
#$TicketComment = "It looks like an app crashed on your computer, and the reason for the crash indicates a Windows component is at fault.`n`nI'll repair your system now."
#Create-Syncro-Ticket-Comment -TicketIdOrNumber $TicketID -Subject "Update" -Body $TicketComment -Hidden $false -DoNotEmail $false
#
## Bill time to the ticket
#$BillableTimeInMinutes = 15
#$StartAt = (Get-Date).AddMinutes(-$BillableTimeInMinutes).ToString("o")
#$TimeEntryNote = "Analysis indicates crash caused by Windows component(s). Scanning and repairing corruption in the system."
#Create-Syncro-Ticket-TimerEntry -TicketIdOrNumber $TicketID -StartTime $StartAt -DurationMinutes $BillableTimeInMinutes -Notes $TimeEntryNote -UserIdOrEmail $TimeAttributedToUser -ChargeTime "true"
#
## Fix Windows Health
#$FixWindowsHealth = Download-File -URL 'https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Fix-WindowsHealth.ps1'
#. $FixWindowsHealth
#If ( Test-Path $FixWindowsHealth ) { Remove-Item $FixWindowsHealth -Force -ErrorAction SilentlyContinue }
+49
View File
@@ -0,0 +1,49 @@
If ( Is64Bit ) { $BlueScreenView = $Global:ToolsDirectory + '\BlueScreenView-x64.exe' } Else { $BlueScreenView = $Global:ToolsDirectory + '\BlueScreenView-x86.exe' }
If ( !(Test-Path $BlueScreenView) ) { Write-Error "File does not exist: ${BlueScreenView}" ; Return }
# Remove existing BSOD report
$BSODReport = $Global:OutputDirectory + '\BSODReport.txt'
If ( Test-Path $BSODReport ) { Write-Output "Deleting existing BSOD report: ${BSODReport}" ; Remove-Item $BSODReport -Force -ErrorAction SilentlyContinue }
# Run BlueScreenView
Start-Process $BlueScreenView -ArgumentList "/stext ${BSODReport} /sort ~1" -Wait
# Wait for BSOD report
For ($i = 0; $i -lt 1200; $i++) {
If ( Test-Path $BSODReport ) { Break }
Start-Sleep -Seconds 1
}
# Exit if the BSOD report was not created
If ( !(Test-Path $BSODReport) ) { Write-Output "BSOD report was not found: ${BSODReport}" ; Return }
# Exit if the BSOD report is empty
If ( (Get-Content -Path $BSODReport).Length -eq 0 ) {
Write-Output "BSOD report is empty: ${BSODReport}"
Remove-Item $BSODReport -Force
Return
}
# Create ticket
$BSODReportContent = $(Get-Content -Path $BSODReport) -join "`n"
New-RMMTicket -Subject "System Crash on ${Env:COMPUTERNAME}" -InitialIssue $BSODReportContent
# Update the ticket
New-TicketComment -Comment "It looks like your computer crashed. I'll get this resolved as soon as possible."
# Wait a bit
Start-Sleep -Seconds 120
# Setup providers for matching BSOD report content
$ProviderMicrosoft = 'Microsoft|Windows'
# Get the provider data from the most recent BSOD
$CulpritProvider = $BSODReportContent[12..13] -join '`n'
Switch ( $CulpritProvider ) {
{ $_ -match $ProviderMicrosoft } {
New-TicketComment -Comment "I've analyzed the crash report and it indicates a problem with a Windows component. I'll repair the system now."
Source-PSScript -ScriptName "Fix-WindowsHealth"
}
}
+3 -3
View File
@@ -3,12 +3,12 @@ $RegKey = 'HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\ImmersiveShell'
## Make sure endpoint is running at least Windows 8
If ( IsWindowsVersion -ge '6.3' )
{
LogMsg "Disabling tabled mode"
Write-Output "Disabling tabled mode"
Try
{
Set-ItemProperty $RegKey -Name 'TabletMode' -Value 0
Set-ItemProperty $RegKey -Name 'SignInMode' -Value 1
}
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
Else { LogMsg "Tablet mode is only supported on Windows 8 and higher" }
Else { Write-Output "Tablet mode is only supported on Windows 8 and higher" }
+17
View File
@@ -0,0 +1,17 @@
# Exit if the endpoint is a server edition
Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -Property ProductType | ForEach-Object { If ( $_.ProductType -gt 1 ) { Return } }
# Exit if the endpoint is not running at least Windows 10
If ( IsWindowsVersion -lt "10.0" ) { Return }
# Exit if the endpoint is not running the Pro/Enterprise version of Windows
$Edition = ($(systeminfo | findstr /B /C:"OS Name") -replace "OS Name:","").Trim()
If ( ($Edition -notmatch "Pro") -and ($Edition -notmatch "Enterprise") ) { Return }
# Exit if Hyper-V is already enabled
If ( (Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V).State -eq "Enabled" ) { Return }
# Endpoint is running the correct version of Windows, enabling Hyper-V features
Write-Output "Enabling Hyper-V features"
Try { Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All -NoRestart }
Catch { Write-Error $_.Exception.Message }
+7 -7
View File
@@ -5,21 +5,21 @@ $FolderNewName = "${FolderToRename}${RenameSuffix}"
# Kill processes that can interfere
'reg', 'AgentPackageTaskManagement' | ForEach-Object { Get-Process -Name $_ -ErrorAction SilentlyContinue | Stop-Process -Force }
LogMsg "Stopping Atera agent"
Write-Output "Stopping Atera agent"
Stop-Service -Name 'AteraAgent' -Force
If ( Test-Path $FolderNewName ) {
LogMsg "Deleting previously renamed folder: ${FolderNewName}"
Write-Output "Deleting previously renamed folder: ${FolderNewName}"
Try { Remove-Item $FolderNewName -Recurse -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
LogMsg "Stopping AgentPackageMonitoring process"
Write-Output "Stopping AgentPackageMonitoring process"
Get-Process -Name "AgentPackageMonitoring" -ErrorAction SilentlyContinue | Stop-Process -Force
LogMsg "Renaming ${FolderToRename}"
Write-Output "Renaming ${FolderToRename}"
Try { Rename-Item $FolderToRename $FolderNewName -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
LogMsg "Starting Atera agent"
Write-Output "Starting Atera agent"
Start-Service -Name 'AteraAgent'
+10 -10
View File
@@ -1,30 +1,30 @@
. $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1')))
# https://knowledge.autodesk.com/search-result/caas/downloads/content/licensing-support-tool.html
$URL = 'https://knowledge.autodesk.com/sites/default/files/file_downloads/AdskLicensingSupportTool-2.2.0.502-win.zip'
#. $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1')))
# https://www.autodesk.com/support/technical/article/caas/tsarticles/ts/6IFPpIksTDfsCzp1JwIH5k.html
$URL = 'https://damassets.autodesk.net/content/dam/autodesk/external-assets/support-articles/licensing-support-tool/AdskLicensingSupportTool-2.3.0.692-win.zip'
# Download the zip archive
$LicensingSupportTool = Download-File -URL $URL
# Extract the downloaded zip file
Unzip-Object $LicensingSupportTool
Expand-Archive -Path $LicensingSupportTool
$WorkingDir = '{0}\{1}' -f (Split-Path -Path $LicensingSupportTool -Parent), "AdskLicensingSupportTool"
$ALST = "${WorkingDir}\AdskLicensingSupportTool.exe"
If ( Test-Path $ALST ) {
LogMsg "Resetting all Autodesk product licenses to named-user licenses"
Write-Output "Resetting all Autodesk product licenses to named-user licenses"
Try { Start-Process $ALST -ArgumentList '-r ALL:User' -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
If ( Test-Path $LicensingSupportTool ) {
LogMsg "Deleting ""${LicensingSupportTool}"""
Write-Output "Deleting ""${LicensingSupportTool}"""
Try { Remove-Item $LicensingSupportTool -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
If ( Test-Path $WorkingDir ) {
LogMsg "Deleting ""${WorkingDir}"""
Write-Output "Deleting ""${WorkingDir}"""
Try { Remove-Item $WorkingDir -Force -Recurse -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
+23
View File
@@ -0,0 +1,23 @@
@echo off
if /I %0 EQU "%~dpnx0" (set LAUNCH=1) else (set LAUNCH=0)
set PROP=ExcludeExplicitO365Endpoint
set KEY=Microsoft\Office\16.0\Outlook\AutoDiscover
echo Disabling Office 365 AutoDiscover...
reg add HKCU\Software\%KEY% /v %PROP% /t REG_DWORD /d 1 /f /reg:32 >nul 2>nul
reg add HKCU\Software\Policies\%KEY% /v %PROP% /t REG_DWORD /d 1 /f /reg:32 >nul 2>nul
reg add HKCU\Software\%KEY% /v %PROP% /t REG_DWORD /d 1 /f /reg:64 >nul 2>nul
reg add HKCU\Software\Policies\%KEY% /v %PROP% /t REG_DWORD /d 1 /f /reg:64 >nul 2>nul
if %ERRORLEVEL% GTR 0 (
if %LAUNCH% GTR 0 color 0e
echo ERROR: This script failed to run. Please make sure to Run As Administrator
reg delete HKCU\Software\%KEY% /v %PROP% /f /reg:32 >nul 2>nul
reg delete HKCU\Software\Policies\%KEY% /v %PROP% /f /reg:32 >nul 2>nul
reg delete HKCU\Software\%KEY% /v %PROP% /f /reg:64 >nul 2>nul
reg delete HKCU\Software\Policies\%KEY% /v %PROP% /f /reg:64 >nul 2>nul
echo Changes have been reverted.
if %LAUNCH% GTR 0 pause
goto :EOF
)
echo Successfully disabled Office 365 AutoDiscover!
echo Please restart your comptuer.
if %LAUNCH% GTR 0 pause
+2 -2
View File
@@ -1,3 +1,3 @@
LogMsg "Clearing Outlook auto complete cache"
Write-Output "Clearing Outlook auto complete cache"
Try { Start-Process "outlook.exe" -ArgumentList "/CleanAutoCompleteCache" -ErrorAction Stop }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
+6 -6
View File
@@ -6,16 +6,16 @@ If ( IsWindowsVersion -ge "10.0" ) {
$FILEDIR = Split-Path -Path $FILE -Parent
If ( Test-Path $FILE ) {
LogMsg "Clearing Quick Access links"
Write-Output "Clearing Quick Access links"
Try { Remove-Item $FILE -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
} ElseIf ( Test-Path $FILEDIR ) {
LogMsg "Standard Quick Access link list is not found, clearing out all automatic destinations instead"
Write-Output "Standard Quick Access link list is not found, clearing out all automatic destinations instead"
Try { Get-ChildItem "${FILEDIR}\*.*" | Where-Object { -not $_.PSIsContainer } | Remove-Item -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
} Else {
LogMsg "Quick Access lists cannot be found!"
Write-Output "Quick Access lists cannot be found!"
}
} Else {
LogMsg "Quick Access is only available on Windows 10 or greater"
Write-Output "Quick Access is only available on Windows 10 or greater"
}
+3 -1
View File
@@ -2,7 +2,9 @@
setlocal
cls
call :configure-service AteraAgent
call :configure-service Syncro
call :configure-service SyncroLive
call :configure-service SyncroOvermind
call :configure-service SplashtopRemoteService
goto :EOF
+17
View File
@@ -0,0 +1,17 @@
# Dell Optimizer
If ( Test-Path "${Env:ProgramFiles}\Dell\DellOptimizer\do-cli.exe" ) { $DellOptimizerCli = "${Env:ProgramFiles}\Dell\DellOptimizer\do-cli.exe" }
ElseIf ( Test-Path "${Env:ProgramFiles(x86)}\Dell\DellOptimizer\do-cli.exe" ) { $DellOptimizerCli = "${Env:ProgramFiles(x86)}\Dell\DellOptimizer\do-cli.exe" }
If ( $DellOptimizerCli ) {
Write-Output "Dell Optimizer"
Try {
# Disable Network Settings
Write-Output "Disabling network settings"
Start-Process -FilePath "C:\Program Files\Dell\DellOptimizer\do-cli.exe" -ArgumentList '/configure -name=Network.State -value=false' -Wait
# Disable Presence Detection
Write-Output "Disabling presence detection"
Start-Process -FilePath "C:\Program Files\Dell\DellOptimizer\do-cli.exe" -ArgumentList '/configure -name=PresenceDetection.State -value=false' -Wait
}
Catch { Write-Error $_.Exception.Message }
} Else { Write-Output "Dell Optimizer command line application cannot be found!" }
+2
View File
@@ -0,0 +1,2 @@
# Disable all OneDrive notifications
[Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings\Microsoft.SkyDrive.Desktop","Enabled",0,[Microsoft.Win32.RegistryValueKind]::DWord)
+24
View File
@@ -0,0 +1,24 @@
@echo off
if /I %0 EQU "%~dpnx0" (set LAUNCH=1) else (set LAUNCH=0)
set PROP=HideNewOutlookToggle
set KEY=Microsoft\Office\16.0\Outlook\Options\General
set ARGS=/v %PROP% /t REG_DWORD /d 1 /f
echo Disabling New Outlook toggle...
reg add HKCU\Software\%KEY% %ARGS% /reg:32 >nul 2>nul
reg add HKCU\Software\Policies\%KEY% %ARGS% /reg:32 >nul 2>nul
reg add HKCU\Software\%KEY% %ARGS% /reg:64 >nul 2>nul
reg add HKCU\Software\Policies\%KEY% %ARGS% /reg:64 >nul 2>nul
if %ERRORLEVEL% GTR 0 (
if %LAUNCH% GTR 0 color 0e
echo ERROR: This script failed to run. Please make sure to Run As Administrator
reg delete HKCU\Software\%KEY% /v %PROP% /f /reg:32 >nul 2>nul
reg delete HKCU\Software\Policies\%KEY% /v %PROP% /f /reg:32 >nul 2>nul
reg delete HKCU\Software\%KEY% /v %PROP% /f /reg:64 >nul 2>nul
reg delete HKCU\Software\Policies\%KEY% /v %PROP% /f /reg:64 >nul 2>nul
echo Changes have been reverted.
if %LAUNCH% GTR 0 pause
goto :EOF
)
echo Successfully disabled New Outlook toggle!
echo Please restart your comptuer.
if %LAUNCH% GTR 0 pause
+5
View File
@@ -0,0 +1,5 @@
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Outlook\Options\General]
"HideNewOutlookToggle"=dword:00000001
+2
View File
@@ -0,0 +1,2 @@
# Disable telemetry
[Microsoft.Win32.Registry]::SetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DataCollection","AllowTelemetry",0,[Microsoft.Win32.RegistryValueKind]::DWord)
+34
View File
@@ -0,0 +1,34 @@
# This script requires the $Edition variable is set from the RMM script
If (! $Edition ) { $Edition = 'desktop' }
# Set the correct download URL and agent edition
switch ( $Edition.ToLower() ) {
'server' { $AgentEdition = 'baremetal' }
'desktop' { $AgentEdition = 'desktop' }
'vm' { $AgentEdition = 'vmedition' }
default { $AgentEdition = 'desktop' }
}
# Install and import additional Powershell module
Import-MSP360Module
# Exit this script if the agent is not installed
If ( !($(Get-MBSAgent -ErrorAction SilentlyContinue)) ) {
Microsoft.Powershell.Utility\Write-Output "Backup agent is not installed. This script will now exit."
Return
}
# Set the agent edition
Write-Output "Setting Emberkom Cloud Backup agent edition to ""$AgentEdition"""
Try {
Switch ([string]::IsNullOrEmpty($MasterPassword)) {
$true { Set-MBSAgentSetting -Edition $AgentEdition -Verbose }
$false { Set-MBSAgentSetting -Edition $AgentEdition -MasterPassword $(ConvertTo-SecureString -String $MasterPassword -AsPlainText -Force) -Verbose }
}
}
Catch { Write-Error $_.Exception.Message }
# Delete the desktop icon
Write-Output "Removing desktop icon"
Remove-Item -Path "${Env:PUBLIC}\Desktop\Emberkom Backup.LNK" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "${Env:PUBLIC}\Desktop\Emberkom Cloud Backup.LNK" -Force -ErrorAction SilentlyContinue
+11
View File
@@ -0,0 +1,11 @@
# Disable unattended sleep timeout (if computer was woken up by an unattended process, this timer kicks in)
# The default value for this setting is 120 (2 minutes)
# Aliases:
# SUB_SLEEP = 238c9fa8-0aad-41ed-83f4-97be242c8f20
# UNATTENDSLEEP = 7bc4a2f9-d8fc-4469-b07b-33eb785aaca0
$UnattendedSleepTimeout = 0
Try {
Start-Process "powercfg.exe" -ArgumentList "/setacvalueindex SCHEME_CURRENT SUB_SLEEP UNATTENDSLEEP ${UnattendedSleepTimeout}" -Wait
Start-Process "powercfg.exe" -ArgumentList "/setdcvalueindex SCHEME_CURRENT SUB_SLEEP UNATTENDSLEEP ${UnattendedSleepTimeout}" -Wait
}
Catch { Write-Error $_.Exception.Message }
+42
View File
@@ -0,0 +1,42 @@
$key = "HKLM:\SYSTEM\CurrentControlSet\Control\GraphicsDrivers"
$name = "HwSchMode"
# Check to see if GPU scheduling is enabled
$HardwareGPUSchedulingEnabled = $false
If ( Test-RegistryProperty -Path $key -Name $name ) {
$setting = (Get-ItemPropertyValue -Path $key -Name $name -ErrorAction SilentlyContinue)
If ( $setting -eq 2 ) {
Write-Output "Hardware assisted GPU scheduling is already enabled"
$HardwareGPUSchedulingEnabled = $true
}
}
If ( $HardwareGPUSchedulingEnabled -eq $false ) {
Write-Output "Hardware assisted GPU scheduling is disabled"
$HardwareGPUSchedulingEnabled = $false
}
# Find a GPU in the system so we can enable GPU scheduling
If ( $HardwareGPUSchedulingEnabled -eq $false ) {
Write-Output " - Attempting to find a discrete Nvidia or AMD GPU"
$GPUs = (Get-WmiObject Win32_VideoController).Name
$FoundDiscreteGPU = $false
ForEach ( $gpu in $GPUs ) {
If ( $gpu -ilike "*NVIDIA*" ) {
$FoundDiscreteGPU = $true
}
}
# If we found a discrete GPU, enable hardware assisted GPU scheduling
If ( $FoundDiscreteGPU -eq $true ) {
Write-Output " - GPU found!"
Write-Output "Enabling hardware assisted GPU scheduling"
Try {
New-ItemProperty -Path $key -Name $name -PropertyType DWord -Value 2 -Force -ErrorAction Stop
Write-Output "A restart is required before this change will take effect"
}
Catch { Write-Error $_.Exception.Message }
} Else {
Write-Output " - No GPU was found"
Write-Output "Hardware assisted GPU scheduling will not be enabled"
}
}
+4
View File
@@ -0,0 +1,4 @@
If ( $null -ne $(Get-ComputerRestorePoint) ) { Write-Output "System Restore is already enabled" ; Exit }
Write-Output "Enabling System Restore"
Try { Enable-ComputerRestore -Drive $Env:SystemDrive }
Catch { Write-Error $_.Exception.Message }
+30
View File
@@ -0,0 +1,30 @@
# Exit script if required variables are not defined
If ( !$TdrLevel ) { Write-Error "TdrLevel must be defined, this script will now exit." ; Exit }
If ( !$TdrDelay ) { Write-Error "TdrDelay must be defined, this script will now exit." ; Exit }
# Set the root registry path
$RegPath = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers'
# Get/set the TDR level
$ExistingTdrLevel = [Microsoft.Win32.Registry]::GetValue($RegPath,'TdrLevel',3)
If ( $TdrLevel -ne $ExistingTdrLevel ) {
# Set the TDR level
# Note: 1 = Disable, 3 = Recover (default)
Write-Output "Existing TDR level: ${ExistingTdrLevel}"
Write-Output "Changing TDR level to: ${TdrLevel}"
[Microsoft.Win32.Registry]::SetValue($RegPath,'TdrLevel',$TdrLevel,[Microsoft.Win32.RegistryValueKind]::DWord)
} Else {
Write-Output "TDR level is already set to ${TdrLevel}"
}
# Get/set the TDR delay
$ExistingTdrValue = [Microsoft.Win32.Registry]::GetValue($RegPath,'TdrDelay',2)
If ( $TdrDelay -ne $ExistingTdrValue ) {
# Setting TDR timeout to $TdrDelay seconds
# Note: 2 = Default
Write-Output "Existing TDR delay: ${ExistingTdrValue}"
Write-Output "Changing TDR delay to: ${TdrDelay}"
[Microsoft.Win32.Registry]::SetValue($RegPath,'TdrDelay',$TdrDelay,[Microsoft.Win32.RegistryValueKind]::DWord)
} Else {
Write-Output "TDR delay is already set to ${TdrDelay}"
}
+1 -1
View File
@@ -1,2 +1,2 @@
Try { Start-Process "gpupdate" -ArgumentList '/force' -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
+1 -1
View File
@@ -1,2 +1,2 @@
Stop-MemoryHog -AppName 'searchapp' -MemoryLimit 2048
Stop-MemoryHog -AppName 'AgentPackageProgramManagement' -MemoryLimit 1024
#Stop-MemoryHog -AppName 'AgentPackageProgramManagement' -MemoryLimit 1024
+1
View File
@@ -0,0 +1 @@
Get-AppXPackage *WindowsStore* -AllUsers | ForEach-Object {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml"}
+8 -8
View File
@@ -5,23 +5,23 @@ If ( Test-Path "${Env:LocalAppData}\Microsoft\Teams\Update.exe" ) {
$ProcessList = (Get-Process -Name 'teams' -ErrorAction SilentlyContinue)
If ( $ProcessList ) {
LogMsg "Stopping Microsoft Teams"
Write-Output "Stopping Microsoft Teams"
Try { $ProcessStopped = $true ; $ProcessList | Stop-Process -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
} Else { $ProcessStopped = $false }
# Remove all cached content
$CacheDir = "${Env:AppData}\Microsoft\Teams"
If ( Test-Path $CacheDir ) {
LogMsg "Removing all cached data for Microsoft Teams"
Write-Output "Removing all cached data for Microsoft Teams"
Try { Remove-Item $CacheDir -Recurse -Force }
Catch { LogErr $_.Exception.Message }
} Else { LogMsg "Could not clear cached data for Microsoft Teams because the directory could not be found: ${CacheDir}" }
Catch { Write-Error $_.Exception.Message }
} Else { Write-Output "Could not clear cached data for Microsoft Teams because the directory could not be found: ${CacheDir}" }
# Restart Teams if we needed to close it before clearing the cache
If ( $ProcessStopped ) {
LogMsg "Starting Microsoft Teams"
Write-Output "Starting Microsoft Teams"
Try { Start-Process "${Env:LocalAppData}\Microsoft\Teams\Update.exe" -ArgumentList "--processStart ""Teams.exe""" }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
} Else { LogMsg "Cannot clear cache for Microsoft Teams because it isn't installed" }
} Else { Write-Output "Cannot clear cache for Microsoft Teams because it isn't installed" }
+3 -3
View File
@@ -1,8 +1,8 @@
$REG_PATH = 'HKCU:\SOFTWARE\Microsoft\Exchange'
$REG_PROP = 'AlwaysUseMSOAuthForAutoDiscover'
If ( !((Get-ItemProperty -Path $REG_PATH -Name $REG_PROP -ErrorAction SilentlyContinue).$REG_PROP) ) {
LogMsg "Adding registry entry: ""${REG_PATH}\${REG_PROP}"" = 1"
Write-Output "Adding registry entry: ""${REG_PATH}\${REG_PROP}"" = 1"
Try { New-ItemProperty -Path $REG_PATH -Name $REG_PROP -PropertyType DWord -Value 1 | Out-Null }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
Else { LogMsg "The registry path already exists: ""${REG_PATH}\${REG_PROP}""" }
Else { Write-Output "The registry path already exists: ""${REG_PATH}\${REG_PROP}""" }
+4
View File
@@ -0,0 +1,4 @@
# Note: $FileTypes must be set by the calling script
# Unblock attachment types
[Microsoft.Win32.Registry]::SetValue("HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Outlook\Security","Level1Remove","${FileTypes}",[Microsoft.Win32.RegistryValueKind]::String)
+2 -2
View File
@@ -34,10 +34,10 @@ Function IsWindowsVersion {
# Make sure we're running at least Windows 10 1803 but not higher than Windows 10 21H2
# This is because the Group Policy setting used below was introduced in 1803, so anything before that won't have any effect
If ( (IsWindowsVersion -ge "10.0.17134") -and (IsWindowsVersion -le "10.0.19044") ) {
If ( (IsWindowsVersion -ge "10.0.17134") -and (IsWindowsVersion -le "10.0.19045") ) {
# This is the version of Windows 10 you want to update to, but no further
$MaxWindowsVersion = "21H2"
$MaxWindowsVersion = "22H2"
$BaseRegPath = "HKLM:SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate"
If (-not (Test-Path $BaseRegPath) ) { New-Item -Path $BaseRegPath -Force | Out-Null }
+6 -3
View File
@@ -6,13 +6,16 @@ set /a REBOOT=0
echo.
echo Configuring required services to automatically start in Safe Mode with Networking.
call :configure-service AteraAgent
call :configure-service Syncro
call :configure-service SyncroLive
call :configure-service SyncroOvermind
call :configure-service SplashtopRemoteService
call :configure-service "Cloud Backup Service Remote Management"
:schedule-normal-reboot
call :reset-errorlevel
echo Configuring system to reboot into Normal Mode at next reboot
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Runonce" /v "*RestartNormalMode" /t REG_SZ /d "%SystemRoot%\System32\bcdedit.exe /deletevalue {current} safeboot" /f > nul 2>&1
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Runonce" /v "*RestartNormalMode" /t REG_SZ /d "bcdedit /deletevalue {current} safeboot" /f > nul 2>&1
if %ERRORLEVEL% GTR 0 (
set /a REBOOT=%REBOOT%+1
echo ERROR: could not configure reboot into normal mode
@@ -20,7 +23,7 @@ if %ERRORLEVEL% GTR 0 (
:configure-reboot-safemodewithnetworking
call :reset-errorlevel
"%SystemRoot%\System32\bcdedit.exe" /set {current} safeboot network
bcdedit /set {current} safeboot network
if %ERRORLEVEL% GTR 0 (
set /a REBOOT=%REBOOT%+1
echo ERROR: could not restart into Safe Mode with Networking
+32
View File
@@ -0,0 +1,32 @@
# List of services to configure
$Services = @(
"Syncro",
"SyncroLive",
"SyncroOvermind",
"SplashtopRemoteService"
)
# Configure necessary services to restart in Safe Mode with Networking
$Services | ForEach-Object {
If ( Get-Service $_ -ErrorAction SilentlyContinue ) {
Write-Output "Configuring ${_} service to start in Safe Mode with Networking"
Try { New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SafeBoot\Network\${_}" -Force -ErrorAction SilentlyContinue | Out-Null }
Catch { Write-Error "ERROR: could not configure ${_} service to start in Safe Mode with Networking" ; Return }
}
}
# Configure system to reboot into Normal Mode at next reboot
Write-Output "Configuring system to reboot into Normal Mode at next reboot"
Try { [Microsoft.Win32.Registry]::SetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Runonce",'*RestartNormalMode','cmd.exe /c "%SystemRoot%\System32\bcdedit.exe" /deletevalue {default} safeboot',[Microsoft.Win32.RegistryValueKind]::String) }
Catch { Write-Error "ERROR: could not configure reboot into normal mode" ; Return }
# Configure reboot into Safe Mode with Networking
Write-Output "Configuring system to reboot into Safe Mode with Networking"
$command = "${Env:SystemRoot}\System32\bcdedit.exe"
$params = "/set {default} safeboot network".Split(" ")
Try { & "${command}" $params }
Catch { Write-Error "ERROR: could not configure reboot into safe mode with networking" ; Return }
# Reboot into Safe Mode with Networking
Write-Output "Rebooting into Safe Mode with Networking"
& "${Env:SystemRoot}\System32\shutdown.exe" -r -f -t 5
+1
View File
@@ -0,0 +1 @@
Get-AppxPackage -AllUsers | ForEach-Object { Add-AppxPackage -DisableDevelopmentMode -Register “$($_.InstallLocation)\AppXManifest.xml” }
+2 -2
View File
@@ -7,9 +7,9 @@ If (! ($(Get-Service -Name 'spooler').StartType -eq "Disabled") ) {
Control-Service -Stop -Name $SERVICE
## Delete all print jobs
LogMsg "Deleting all print jobs from ""${Env:WINDIR}\System32\spool\PRINTERS"""
Write-Output "Deleting all print jobs from ""${Env:WINDIR}\System32\spool\PRINTERS"""
Try { Remove-Item "${Env:WINDIR}\System32\spool\PRINTERS\*" -Recurse }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Start the spooler service
Control-Service -Start -Name $SERVICE
+76
View File
@@ -0,0 +1,76 @@
$DNSHelper = $Global:ToolsDirectory + '\ekdns.exe'
$DNSHelperRequiredVersion = '2.3.0'
$DownloadRequired = $false
If (Test-Path $DNSHelper) {
$versionOutput = (& $DNSHelper 2>&1 | Select-Object -First 1)
If ($versionOutput -match 'v(\d+\.\d+\.\d+)') {
If ($Matches[1] -ne $DNSHelperRequiredVersion) {
$DownloadRequired = $true
}
} Else {
$DownloadRequired = $true
}
} Else {
$DownloadRequired = $true
}
# Download the latest version of ekDNSHelper
If ($DownloadRequired) {
$DNSHelperURL = 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/ekdns-x64-exe/download/ekdns-x64.exe'
If (!(Test-Path ${Env:ProgramFiles(x86)})) {
$DNSHelperURL = 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/ekdns-x86-exe/download/ekdns-x86.exe'
}
If (Test-Path $DNSHelper) { Remove-Item -Path $DNSHelper -Force -ErrorAction SilentlyContinue }
Write-Output "Downloading latest version of ekDNSHelper"
Download-File -URL $DNSHelperURL -File $DNSHelper
}
# Delete the old version of ekDNSHelper if it exists
If ( Test-Path ($Global:ToolsDirectory + '\dnshelper.exe') ) {
Write-Output "Deleting old version of ekDNSHelper: ${Global:ToolsDirectory}\dnshelper.exe"
Remove-Item -Path ($Global:ToolsDirectory + '\dnshelper.exe') -Force -ErrorAction SilentlyContinue
}
$RefreshScript = $Global:ToolsDirectory + '\refresh-local-hosts.bat'
$todaysDate = Get-LongDate
$RefreshScriptHeader = @"
@echo off
REM Refresh IP to name mappings in local hosts file
REM This script was created by an automatic process and should not be edited manually
REM Author: David Yoder, Emberkom LLC (Fix-UpdateLocalHosts.ps1)
REM Updated: ${todaysDate}`n
"@
$Hosts = @(
'auth.services.mspbackups.com',
'ws.mspbackups.com'
)
If ($BackupBucket.Length -ge 2 ) {
$Hosts += $BackupBucket
}
$HostString = ''
ForEach ($Hostname in $Hosts) {
If ($HostString.Length -gt 0) { $HostString = $HostString + ',' + $Hostname }
Else { $HostString = $Hostname }
}
If ( Test-Path $DNSHelper ) {
$commandString = """${DNSHelper}"" add -host ${HostString}"
$content = $RefreshScriptHeader + $commandString
Write-Output "Creating latest refresh script"
If ( Test-Path $RefreshScript ) { Remove-Item -Path $RefreshScript -Force }
Try {
New-Item -Path $RefreshScript -ItemType File -Force | Out-Null
Set-Content -Path $RefreshScript -Value $content
}
Catch { Write-Error $_.Exception.Message }
Write-Output "Updating local hosts file with name-to-IP mappings for the following host(s): ${HostString}"
& "${RefreshScript}"
} Else {
Write-Error "Cannot find the DNS Helper executable: ""${DNSHelper}"""
}
+58 -19
View File
@@ -1,29 +1,68 @@
# Run the System File Checker to scan for and repair any problems with protected system files
LogMsg "Running System File Checker"
$SFCOutput = $(sfc /scannow) | Out-String
LogMsg $SFCOutput
# Run SFC to check the filesystem, convert the result from Unicode to UTF8, delete the intermediate $tempFile
$tempFile = New-TemporaryFile
$(sfc.exe /scannow) | Out-File $tempFile
Set-Content -Path $tempFile -Value $(Get-Content -Path $tempFile -Encoding Unicode) -Encoding UTF8
$SFCOutput = Get-Content -Path $tempFile
$TicketComment = "SFC Output:`n"
ForEach ( $line in $SFCOutput ) {
If ( $line -notmatch 'Verification \d{1,2}% complete' ) { $TicketComment = $TicketComment + $line + "`n" }
}
New-TicketComment -Comment $TicketComment -Hidden -DoNotEmail
Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
# Examine $SFCOutput for integrity violations, if found make sure chkdsk runs at the next boot
Switch ( $SFCOutput ) {
{ $_ -match 'Windows Resource Protection found integrity violations' } { $ScheduleCheckDisk = $true }
{ $_ -match 'Windows Resource Protection did not find any integrity violations.' } { $ScheduleCheckDisk = $false }
{ $_ -match 'There is a system repair pending which requires reboot to complete.' } {
$ScheduleCheckDisk = $false
New-TicketComment -Comment "System repair is already pending. Reboot needs to occur first, then SFC should be run again." -Hidden -DoNotEmail
}
}
# Run enhanced DISM for Windows 8 and higher endpoints
If ( IsWindowsVersion -ge "6.2" )
{
$BeginDISMRun = $(Get-Date).AddSeconds(-1)
$DISMRun = $false
If ( IsWindowsVersion -ge "6.2" ) {
LogMsg "Beginning repair of Windows Component Store"
Start-Process "dism.exe" -ArgumentList "/online /cleanup-image /restorehealth" -Wait
# Record start of DISM repair
$BeginDISMRun = (Get-Date)
LogMsg "Beginning cleanup of superceded components in Windows Component Store"
Start-Process "dism.exe" -ArgumentList "/online /cleanup-image /startcomponentcleanup" -Wait
Try {
Write-Output "Beginning repair of Windows Component Store"
Start-Process "dism.exe" -ArgumentList "/online /cleanup-image /restorehealth" -Wait
$EndDISMRun = $(Get-Date).AddSeconds(1)
Write-Output "Beginning cleanup of superceded components in Windows Component Store"
Start-Process "dism.exe" -ArgumentList "/online /cleanup-image /startcomponentcleanup" -Wait
# Adding results of DISM log file to this log file
LogMsg "Beginning Windows Component Store log data collection"
$DISMRun = $true
}
Catch { Write-Error $_.Exception.Message }
# Record end of DISM repair
$EndDISMRun = (Get-Date)
# Extract results of DISM repair from system log and add them to this log
Write-Output "Beginning Windows Component Store log data collection"
Get-Content -Path "${Env:WinDir}\Logs\DISM\dism.log" | Select-String -Pattern '^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})' |
ForEach-Object {
If ( ((Get-Date $_.Line.Substring(0,19)) -gt $BeginDISMRun) -and ((Get-Date $_.Line.Substring(0,19)) -le $EndDISMRun) ) {
LogMsg $_.Line
ForEach-Object {
If ( ((Get-Date $_.Line.Substring(0,19)) -gt $BeginDISMRun) -and ((Get-Date $_.Line.Substring(0,19)) -le $EndDISMRun) ) {
Write-Output $_.Line
}
}
LogMsg "Completed Windows Component Store log data collection"
Write-Output "Completed Windows Component Store log data collection"
}
Else { LogMsg "Online image cleanup and restoration is only supported on Windows 8 and higher" }
Else { Write-Output "Online image cleanup and restoration is only supported on Windows 8 and higher" }
# Schedule Check Disk if needed
If ( $ScheduleCheckDisk ) {
Write-Output "Scheduling disk repair at next boot"
Start-Process "cmd.exe" -ArgumentList '/C echo y | chkdsk /F /R /X' -Wait
New-TicketComment -Comment "Scheduled disk repair at next boot" -Hidden -DoNotEmail
New-TicketComment -Comment "I've made some changes that will require you to restart your computer.`n`nWhenver you're ready, please restart your computer and allow it 30min to complete the startup process."
} Else {
New-TicketComment -Comment "Disk check was not scheduled to run, as no Windows integrity violations were found." -Hidden -DoNotEmail
New-TicketComment -Comment "Your computer has been repaired. Please restart as soon as you're able."
}
# Bill ticket if action was taken
If ( $DISMRun -or $ScheduleCheckDisk ) { New-TicketTimerEntry -Comment "Corruption was found and has been repaired." }
+132
View File
@@ -0,0 +1,132 @@
<#
Title: Fix-WindowsManagementInsturmentationWMI
Copyright (c) Emberkom LLC
Author: David Yoder
Date: 2023-03-14
Intent and Notes:
This script will attempt to salvage the WMI repository. If that fails, it will
rebuild the repository by recompiling all MOF and MFL files and re-registering
all *.dll and *.exe files that exist in the WBEM directory.
This script will take ~7-10min on mid hardware, and >15min on old/slow hardware.
Microsoft documentation recommends using the following command to rebuild the
WMI repository:
winmgmt /resetrepository
However, in my experience the above command doesn't always fix the issue.
#>
# Exit the script if WMI does not appear to be installed at all
If ( !(Test-Path "${Env:SystemRoot}\System32\wbem") ) { Write-Error "WMI components are not present on this device. Please install them manually." ; Exit 1 }
$WMIService = Get-Service -Name winmgmt -ErrorAction SilentlyContinue
# Exit the script if we cannot find the winmgmt service
If ( !$WMIService ) { Write-Output "The winmgmt service cannot be found, WMI is likely not the problem on this endpoint, no actions will be performed by this script" }#; Exit 1 }
# Exit the script if the winmgmt service is not configured for automatic startup
If ( $WMIService.StartType -ne 'Automatic' ) { Write-Output "The winmgmt service is not configured to automatically start, this is not a default configuration, no actions will be performed by this script" }#; Exit 1 }
# Exit the script if the winmgmt service is not running
If ( $WMIService.Status -ne 'Running' ) { Write-Output "The winmgmt service is configured to start automatically but is not currently running, WMI is likely not the issue on this endpoint, no actions will be performed by this script" }#; Exit 1}
# Function to test if WMI is functioning correctly
Function Test-WMIFunctionality {
# Track the number of failed functionality tests
$WMIErrorLevel = 0
# Specify the WMI class name to query
$Class = 'Win32_OperatingSystem'
# Specify a property in the WMI class that we can test for a value
$Property = 'LastBootUpTime'
# Test that our property has a value, and that the WBEM folder exists.
If ( $null -eq $(Get-CimInstance -ClassName $Class -ErrorAction SilentlyContinue | Select-Object $Property -ErrorAction SilentlyContinue).$Property )
{ Write-Error "Failed to query ${Class} for the value of ${Property}" ; $WMIErrorLevel++ }
# Test that the WMI repository is consistent
$WMIRepoStatus = (winmgmt.exe /verifyrepository)
If ( ($WMIRepoStatus -contains "inconsistent") -or ($WMIRepoStatus -contains "failed" ) )
{ Write-Error "WMI repository is in an inconsistent state" ; $WMIErrorLevel++ }
If ( $WMIErrorLevel -eq 0 ) { Return $true } Else { Return $false }
}
# Guard to exit script if no repair is necessary
If ( Test-WMIFunctionality ) { Write-Output "WMI components are functioning correctly" ; Exit 0 }
# Record the start time of the repair
$StartTime = (Get-Date)
# Attempt to salvage WMI repository and exit script if no further repair is necessary
Write-Output "Attempting to salvage WMI repository"
$WMIRepoSalvage = (winmgmt.exe /salvagerepository)
If ( ($WMIRepoSalvage -notcontains "failed") -and (Test-WMIFunctionality) ) { Write-Output "WMI repository successfully salvaged" ; Exit 0 }
Try {
Write-Output "Failed to salvage WMI repository, attempting manual reinitialization of WMI"
# Disable and stop the winmgmt service
Write-Output "Stopping and disabling WMI service"
Set-Service -Name winmgmt -StartupType Disabled
Stop-Service -Name winmgmt -Force
# Set the WBEM directory for manual refresh of WMI components
$TargetDir = "${Env:WinDir}\System32\wbem"
# Rename the Repository directory to Repo_backup
If ( Test-Path "${TargetDir}\Repo_backup" ) { Remove-Item "${TargetDir}\Repo_backup" -Recurse -Force -ErrorAction SilentlyContinue }
If ( Test-Path "${TargetDir}\Repository" ) { Rename-Item -Path "${TargetDir}\Repository" -NewName "${TargetDir}\Repo_backup" -Force }
# Register all *.dll files
Write-Output "Registering all DLL files in ""${TargetDir}"""
$WMIDlls = Get-ChildItem -Path "${TargetDir}\*" -File -Include *.dll
ForEach ( $dll in $WMIDlls ) {
$path = $dll.FullName
Write-Output " ""${path}"""
Start-Process "regsvr32.exe" -ArgumentList "/s ""${path}""" -Wait
}
# Register all *.exe files except for:
# wbemcntl.exe, wbemtest.exe, mofcomp.exe
Write-Output "Registering all supported EXE files in ""${TargetDir}"""
$WMIExes = Get-ChildItem -Path "${TargetDir}\*" -File -Include *.exe | Where-Object { ($_.BaseName.ToLower() -ne "wbemcntl") -and ($_.BaseName.ToLower() -ne "wbemtest") -and ($_.BaseName.ToLower() -ne "mofcomp") }
ForEach ( $exe in $WMIExes ) {
$path = $exe.FullName
Write-Output " ""${path}"""
Start-Process $path -ArgumentList "/regserver" -Wait
}
# Register all *.mof and *.mfl files
Write-Output "Compiling all MOF and MFL files in ""${TargetDir}"""
$WMIMofs = Get-ChildItem -Path "${TargetDir}\*" -File -Include *.mof,*.mfl
ForEach ( $mof in $WMIMofs ) {
$path = $mof.FullName
Write-Output " ""${path}"""
Start-Process "${TargetDir}\mofcomp.exe" -ArgumentList """${path}""" -Wait
}
}
Catch { Write-Error $_.Exception.Message }
Finally {
# Start the winmgmt service
Write-Output "Enabling and starting WMI service"
Set-Service -Name winmgmt -StartupType Automatic
Start-Service -Name winmgmt
}
# Final test of WMI
If ( !(Test-WMIFunctionality) ) { Write-Output "WMI repair failed. Please restart this device and run this script again" ; Exit 1 }
# Remove Repo_backup directory
If ( Test-Path "${TargetDir}\Repo_backup" ) {
Write-Output "Deleting ""${TargetDir}\Repo_backup"""
Remove-Item -Path "${TargetDir}\Repo_backup" -Recurse -Force -ErrorAction SilentlyContinue
}
$ElapsedTime = (New-TimeSpan -Start $StartTime -End (Get-Date)).ToString("hh\:mm\:ss")
Write-Output "WMI has been successfully repaired! Time elapsed: ${ElapsedTime}"
+5 -5
View File
@@ -1,4 +1,4 @@
LogMsg "Fix-WindowsScriptingComponents.ps1"
Write-Output "Fix-WindowsScriptingComponents.ps1"
## List of all scripting component DLL files
$DLL_FILES = @("${Env:WINDIR}\system32\vbscript.dll",
@@ -22,9 +22,9 @@ ForEach ($dll in $DLL_FILES)
If ( Test-Path $dll )
{
If ( $dll -Match "syswow64" ) { $REGSVR32 = "${Env:WINDIR}\syswow64\regsvr32" } Else { $REGSVR32 = "regsvr32" }
LogMsg "Unregister: ${dll}"
Write-Output "Unregister: ${dll}"
Try { Start-Process "${REGSVR32}" -ArgumentList "/u /s ${dll}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
}
@@ -35,8 +35,8 @@ ForEach ($dll in $DLL_FILES)
{
If ( $dll -Match "syswow64" )
{ $REGSVR32 = "${Env:WINDIR}\syswow64\regsvr32" } Else { $REGSVR32 = "regsvr32" }
LogMsg "Register: ${dll}"
Write-Output "Register: ${dll}"
Try { Start-Process "${REGSVR32}" -ArgumentList "/s ${dll}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
}
+8 -8
View File
@@ -5,18 +5,18 @@ $TARGET_DIR = "${Env:WinDir}\SoftwareDistribution"
$DEST_DIR = "${Env:WinDir}\SoftwareDistribution.old"
## Delete any existing folder from a previous execution of this script
LogMsg "Deleting ""${DEST_DIR}"""
Write-Output "Deleting ""${DEST_DIR}"""
Try { Remove-Item $DEST_DIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Stop Background Intelligent Transfer Services and Windows Update
Control-Service -Stop -Name BITS
Control-Service -Stop -Name wuauserv
## Rename the SoftwareDistribution folder, forcing Windows Update to recreate all metrics and redownload all updates
LogMsg "Renaming ""${TARGET_DIR}"" to ""${DEST_DIR}"""
Write-Output "Renaming ""${TARGET_DIR}"" to ""${DEST_DIR}"""
Try { Rename-Item -Path $TARGET_DIR -NewName $DEST_DIR -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Start Windows Update and Background Intelligent Transfer Services
Control-Service -Start -Name BITS
@@ -25,11 +25,11 @@ Control-Service -Start -Name wuauserv
## Delete SoftwareDistribution.old from this execution of this script
## Comment this section out if you want to leave the renamed folder there for some reason,
## but keep in mind this folder can be very large and takes up a lot of space.
LogMsg "Deleting ""${DEST_DIR}"""
Write-Output "Deleting ""${DEST_DIR}"""
Try { Remove-Item $DEST_DIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Force the Windows Update client to check for new updates
LogMsg "Resetting WSUS authorization and starting update detection"
Write-Output "Resetting WSUS authorization and starting update detection"
Try { Start-Process "${Env:WinDir}\System32\wuauctl.exe" -ArgumentList "/resetauthorization /detectnow" }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
+154
View File
@@ -0,0 +1,154 @@
# Function to update a list of paths
Function Update-ListOfPaths {
param([System.Collections.ArrayList]$List,[string]$Path)
If ( $List -inotcontains $Path ) { $List.Add($Path) | Out-Null }
}
# Function to expand the UserProfile environment variable as used by MSP360, and update $List
Function Add-UserProfilePaths {
param([System.Collections.ArrayList]$List,[string]$Path)
$ChildPath = ($Path -split '%')[2]
# Get a list of all user profile directories
ForEach ( $profile in (Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList') ) {
$profileImagePath = Get-ItemPropertyValue -Path $([System.Environment]::ExpandEnvironmentVariables($profile.PSPath)) -Name ProfileImagePath
# Skip profiles located outside C:\Users as MSP360 doesn't currently include these when using the %UserProfile% variable
If ( !($profileImagePath -ilike "*${Env:SystemDrive}\Users\*") ) { Continue }
# Build the path we're trying to add
$path = Join-Path -Path $profileImagePath -ChildPath $ChildPath
# Skip adding the path if it doesn't exist
If ( !(Test-Path $path) ) { Continue }
# Add the path
Update-ListOfPaths -List $List -Path $path
}
}
Function Use-Path {
param([System.Collections.ArrayList]$List,[string]$Path)
Switch ( $Path ) {
{ $_ -ilike '*%UserProfile%*' } { Add-UserProfilePaths -List $List -Path $Path }
{ $_ -ilike '*%*%*' } { Update-ListOfPaths -List $List -Path $([System.Environment]::ExpandEnvironmentVariables($Path)) }
default { Update-ListOfPaths -List $List -Path $Path }
}
}
# Import MSP360 module
Import-MSP360Module
# Get all backup plans
$BackupPlans = Get-MBSBackupPlan -OutputType Raw
# Track the number of processed backup plans so we can put line breaks between them in the output
$planIter = 0
# Iterate over all backup plans
ForEach ( $plan in $BackupPlans ) {
# Add a line break in the output if we've already processed a backup plan
If ( $planIter -gt 0 ) { Write-Output "`n" }
# Function to process each file
Function ProcessFile {
param([System.IO.FileInfo]$File)
Switch ( $File ) {
# Filter out if name matches an excluded path
{ $null -ne ($ExcludedPaths | Where-Object { $File.FullName.Replace('\','\\') -match $_.Replace('\','\\') }) } { Update-FilteredFiles $File }
# Filter out if file size is greater than plan maximum
{ $File.Length -gt $MaxFileSize } { Update-FilteredFiles $File }
# Add item to $IncludeFiles
default { $IncludedFiles.Add($item.FullName, $item.Length) | Out-Null }
}
}
# Function to add a file to $FilteredFiles
Function Update-FilteredFiles {
param([System.IO.FileInfo]$File)
$FilteredFiles.Add($File.FullName, $File.Length) | Out-Null
}
# Print the plan name that we're currently working on
Write-Output ('Plan Name: {0}' -f ($plan.Name))
# Create array lists to store paths included in the plans
[System.Collections.ArrayList]$IncludedPaths = @()
[System.Collections.ArrayList]$ExcludedPaths = @()
# Create array lists to keep files matching certain patterns
[System.Collections.Hashtable]$FilteredFiles = @{}
[System.Collections.Hashtable]$IncludedFiles = @{}
# Get all included items from plan
If ( $null -ne $plan.Items ) {
ForEach ( $obj in $plan.Items ) {
ForEach ( $path in $obj.PlanItem.Path ) {
Use-Path -List $IncludedPaths -Path $path
}
}
}
# Get all excluded items from plan
If ( $null -ne $plan.ExcludedItems ) {
ForEach ( $obj in $plan.ExcludedItems ) {
ForEach ( $path in $obj.PlanItem.Path ) {
Use-Path -List $ExcludedPaths -Path $path
}
}
}
# Print all paths in $IncludedPaths
If ( $IncludedPaths.Length -gt 0 ) {
Write-Output "Paths to backup:"
ForEach ( $path in ($IncludedPaths | Sort-Object) ) { Write-Output $path }
}
# Define $MaxFileSize if it exists, or set it to the default value if it can't be obtained
If ( $null -ne $plan.MaxFileSize ) { $MaxFileSize = $plan.MaxFileSize } Else { $MaxFileSize = [long]::MaxValue }
# Process each path in $IncludedPaths
ForEach ($path in $IncludedPaths ) {
# Skip paths that don't exist
If ( !(Test-Path $path) ) { Continue }
# If $path is an individual file, process it separately
$item = Get-Item -Path $path -ErrorAction SilentlyContinue
If ( $item -is [System.IO.FileInfo] ) { ProcessFile $item ; Continue }
# Get all children of $path
ForEach ( $item in (Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue) ) {
# Process each file
If ( $item -is [System.IO.FileInfo] ) { ProcessFile $item }
}
}
# Print total files and total size
Write-Output ('Total Files: {0}' -f ($IncludedFiles.Count))
If ( $IncludedFiles.Count -gt 0 ) {
$IncludedFilesSize = 0
ForEach ($len in $IncludedFiles.Values) { $IncludedFilesSize += $len}
Write-Output $('Total size: {0}GB' -f [math]::Round(($IncludedFilesSize/1GB),2) )
}
# Print total excluded files and, optionally, the total size of excluded files as well as their full path
Write-Output ('Total Excluded Files: {0}' -f ($FilteredFiles.Count))
If ( $FilteredFiles.Count -gt 0 ) {
$FilteredFilesSize = 0
ForEach ($len in $FilteredFiles.Values) { $FilteredFilesSize += $len}
Write-Output $('Total size: {0}GB' -f [math]::Round(($FilteredFilesSize/1GB),2) )
Write-Output "All files excluded from plan:"
ForEach ($file in $FilteredFiles.GetEnumerator()) {
Write-Output $file.Name
}
}
$planIter++
}
+104
View File
@@ -0,0 +1,104 @@
#Requires -Version 5.0
<#
.SYNOPSIS
Downloads and runs the DSA manifest collector, with optional upload and cleanup.
.DESCRIPTION
Tools.ps1 must be dot-sourced by the caller first. Set $DSAManifestSource
in the caller's scope before using Run-Script -LivePSScript Get-DSAManifest.
The output path is generated in $Global:OutputDirectory with the filename
dsa-manifest-HOSTNAME-yyyyMMddHHmmss.csv, using the computer name and current local date and time.
$Zip enables ZIP compression; $Upload sends the result to the FileDrop;
$DeleteAfterUpload removes the local result only after a confirmed upload.
All three accept 'true', '$true', 'yes', 'y', or '1' to enable, and
'false', '$false', 'no', 'n', or '0' to disable. Empty values disable the option.
Values are case-insensitive and surrounding whitespace is ignored. When
$Upload is false, the local CSV or ZIP is retained regardless of $DeleteAfterUpload.
#>
If ( [string]::IsNullOrWhiteSpace([string]$DSAManifestSource) ) {
Throw 'Set $DSAManifestSource in the caller before running Get-DSAManifest.'
}
# Normalize RMM text values without treating every nonempty string as true.
$DsaOptions = @{
Zip = ConvertTo-RmmBoolean -Value $Zip -VariableName 'Zip'
Upload = ConvertTo-RmmBoolean -Value $Upload -VariableName 'Upload'
DeleteAfterUpload = ConvertTo-RmmBoolean -Value $DeleteAfterUpload -VariableName 'DeleteAfterUpload'
}
$DsaZipEnabled = $DsaOptions.Zip
$DSAManifestOutput = Join-Path -Path $Global:OutputDirectory -ChildPath (
'dsa-manifest-{0}-{1}.csv' -f $Env:COMPUTERNAME, (Get-Date -Format 'yyyyMMddHHmmss')
)
New-Item -Path $Global:OutputDirectory -ItemType Directory -Force -ErrorAction Stop | Out-Null
# Select the executable for the OS, even when the RMM uses 32-bit PowerShell.
If ( [Environment]::Is64BitOperatingSystem ) {
$DsaDownloadUrl = 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/dsa-collect-windows-amd64-exe/download/dsa-collect-windows-amd64.exe'
}
Else {
$DsaDownloadUrl = 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/dsa-collect-windows-386-exe/download/dsa-collect-windows-386.exe'
}
$DsaExecutablePath = Join-Path -Path $Global:ToolsDirectory -ChildPath 'dsa-collect.exe'
New-Item -Path $Global:ToolsDirectory -ItemType Directory -Force -ErrorAction Stop | Out-Null
Write-Output "Downloading dsa-collect.exe to ${DsaExecutablePath}"
$DsaDownloadedFile = Download-FileDirectly -URL $DsaDownloadUrl -File $DsaExecutablePath -ErrorAction Stop
# Require a returned path and a nonempty download before starting the collector.
If ( [string]::IsNullOrWhiteSpace([string]$DsaDownloadedFile) -or
!(Test-Path -LiteralPath $DsaExecutablePath -PathType Leaf) ) {
Throw "Failed to download dsa-collect.exe to ${DsaExecutablePath}"
}
If ( (Get-Item -LiteralPath $DsaExecutablePath -ErrorAction Stop).Length -eq 0 ) {
Throw "The downloaded dsa-collect.exe is 0 bytes: ${DsaExecutablePath}. The collector was not started. Check the download response and client TLS/BITS settings."
}
$DsaArguments = @('--source', [string]$DSAManifestSource, '--output', [string]$DSAManifestOutput)
If ( $DsaZipEnabled ) {
$DsaArguments += '--zip'
}
# Quote native arguments explicitly, including paths ending in a backslash.
# Start-Process joins its argument list without adding the required quoting.
$DsaCommandLine = ($DsaArguments | ForEach-Object {
'"{0}"' -f ($_ -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1')
}) -join ' '
$DsaCollectionTime = Get-Date
Write-Output "Running dsa-collect.exe (ZIP compression: ${DsaZipEnabled})"
$DsaProcess = Start-Process -FilePath $DsaExecutablePath -ArgumentList $DsaCommandLine `
-Wait -PassThru -WindowStyle Hidden -ErrorAction Stop
If ( $DsaProcess.ExitCode -ne 0 ) {
Throw "dsa-collect.exe failed with exit code $($DsaProcess.ExitCode)"
}
$DsaResultPath = $DSAManifestOutput
If ( $DsaZipEnabled ) {
$DsaResultPath = [System.IO.Path]::ChangeExtension($DSAManifestOutput, '.zip')
}
If ( !(Test-Path -LiteralPath $DsaResultPath -PathType Leaf) ) {
Throw "dsa-collect.exe did not create the expected output: ${DsaResultPath}"
}
Write-Output "DSA manifest created: ${DsaResultPath}"
If ( $DsaOptions.Upload ) {
Write-Output "Uploading DSA manifest: ${DsaResultPath}"
$DsaUploadReceipt = Upload-File -Path $DsaResultPath `
-Subject "DSA manifest file uploaded from ${Env:COMPUTERNAME}" `
-Message "DSA collection performed on ${DSAManifestSource} at $($DsaCollectionTime.ToString('yyyy-MM-dd HH:mm:ss zzz'))" `
-ErrorAction Stop
If ( [string]::IsNullOrWhiteSpace([string]$DsaUploadReceipt.AttachmentId) -or
[string]::IsNullOrWhiteSpace([string]$DsaUploadReceipt.Status) ) {
Throw 'The upload did not return a confirmation receipt. The local manifest has been retained.'
}
Write-Output "DSA manifest uploaded: ${DsaResultPath}"
If ( $DsaOptions.DeleteAfterUpload ) {
Remove-Item -LiteralPath $DsaResultPath -Force -ErrorAction Stop
Write-Output "Deleted uploaded DSA manifest: ${DsaResultPath}"
}
$DsaUploadReceipt
}
+59
View File
@@ -0,0 +1,59 @@
# This script depends on the host, port, username, and password being defined in the calling script
If ( -not($SFTPHost) ) { Write-Error "SFTP host is not defined, this script will exit." ; Exit }
If ( -not($SFTPPort) ) { Write-Error "SFTP port is not defined, this script will exit." ; Exit }
If ( -not($SFTPUser) ) { Write-Error "SFTP username is not defined, this script will exit." ; Exit }
If ( -not($SFTPPass) ) { Write-Error "SFTP password is not defined, this script will exit." ; Exit }
$ExportedEvents = "${Env:TEMP}\${Env:COMPUTERNAME}_events.csv"
# Install Posh-SSH module
If ( !(Get-Module -ListAvailable -Name Posh-SSH) ) {
Write-Output "Installing Posh-SSH module"
Install-Module -Name Posh-SSH -Force
}
# Cleanup previous runs
If ( Test-Path $ExportedEvents ) {
Write-Output "Deleting exported events from previous script run ""${ExportedEvents}"""
Try { Remove-Item $ExportedEvents -Force -ErrorAction Stop }
Catch { Write-Output "Cannot remove files from previous script execution. This script cannot run." ; Exit }
}
# Get all available log files
$Logs = Get-WinEvent -ListLog *
# Export all significant events from all available log files
ForEach ($Log in $Logs) {
$LogName = $Log.LogName
Write-Output "Exporting events from: ${LogName}"
Try { Get-WinEvent -LogName $LogName -FilterXPath "Event/System[Level=1 or Level=2 or Level=3]" -ErrorAction Stop | Select-Object LogName,ProviderName,Level,Id,Message | Export-Csv -Path "${ExportedEvents}" -Append -NoTypeInformation -ErrorAction Stop }
Catch { Write-Error $_.Exception.Message }
}
# Deduplicate exported events
Write-Output "Deduplicating and sorting all exported events"
Try {
$TempEvents = Import-Csv $ExportedEvents -ErrorAction Stop | Sort-Object LogName,ProviderName,Id -Unique
$TempEvents | Sort-Object -Property LogName,ProviderName | Export-Csv $ExportedEvents -NoTypeInformation -ErrorAction Stop
}
Catch { Write-Error $_.Exception.Message }
# Upload to SFTP site
If ( Test-Path $ExportedEvents ) {
$SFTPCred = New-Object System.Management.Automation.PSCredential($SFTPUser,$SFTPPass)
# Wait for a random amount of time before starting the connection and uploading
Start-Sleep -Seconds $(Get-Random -Minimum 1 -Maximum 120)
# Make the connection and upload the file
$SFTPSession = New-SFTPSession -ComputerName $SFTPHost -Credential $SFTPCred -AcceptKey -Port $SFTPPort
Write-Output "Uploading events to SFTP site"
Try { Set-SFTPItem -SessionId $SFTPSession.SessionId -Path $ExportedEvents -Destination . -Force }
Catch { Write-Error $_.Exception.Message }
Finally { Remove-SFTPSession -SessionId $SFTPSession.SessionId }
}
# Cleanup
Write-Output "Deleting ""${ExportedEvents}"""
Remove-Item $ExportedEvents -Force -ErrorAction SilentlyContinue
+6 -6
View File
@@ -9,12 +9,12 @@ If ( Test-Path $FILE ) { Clear-Content -Path $FILE -Force }
Function Write-LineToFile {
param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Line)
If ( -not (Test-Path $Path) ) {
LogMsg "Creating file ""${Path}"""
Write-Output "Creating file ""${Path}"""
Try { New-Item -Path $Path -ItemType File -Force | Out-Null }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
Try { Add-Content -Path $Path -Value $Line }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Function to test whether or not an item has a specified property
@@ -22,7 +22,7 @@ Function Test-ItemProperty {
param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Name)
Try {
$test = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue
If (($test -eq $null) -or ($test.Length -eq 0)) { Return $false } Else { Return $true }
If (($null -eq $test) -or ($test.Length -eq 0)) { Return $false } Else { Return $true }
}
Catch { Return $false }
}
@@ -59,7 +59,7 @@ Function Get-ItemPropertyInfo {
## Make sure $formatstring has a value, if not just try to cast directly to a datetime
Try {
If ( $formatstring -eq $null ) {
If ( $null -eq $formatstring ) {
$date = [datetime]$retval
} Else {
$date = [datetime]::ParseExact($retval,$formatstring,$null)
@@ -68,7 +68,7 @@ Function Get-ItemPropertyInfo {
## Set $retval to a standard datetime format
$retval = $date.ToString('yyyy-MM-dd')
}
Catch { LogErr $_.Exception.Message; $retval = $origRetval }
Catch { Write-Error $_.Exception.Message; $retval = $origRetval }
}
Default {}
}
File diff suppressed because it is too large Load Diff
+528
View File
@@ -0,0 +1,528 @@
# Based on the script by lwhitelock, which is based on the original script by Kelvin Tegelaar https://github.com/KelvinTegelaar/AutomaticDocumentation
#####################################################################
#
# Active Directory Details to Hudu
#
# $HuduAPIKey must be supplied by RMM script
# $HuduBaseDomain is the base domain of your Hudu instance (without a trailing /)
# and must be supplied by RMM script
# $CompanyName must exactly match the name of the company in Hudu
# and must be supplied by RMM script
# This is the name of the Hudu Asset Layout you want to use.
$HuduAssetLayoutName = "Active Directory"
#####################################################################
# Get the Hudu API Module if not installed
if (Get-Module -ListAvailable -Name HuduAPI) {
Import-Module HuduAPI
} else {
Install-Module HuduAPI -Force
Import-Module HuduAPI
}
# Set Hudu logon information
New-HuduAPIKey $HuduAPIKey
New-HuduBaseUrl $HuduBaseDomain
Function Get-RegistryValue
{
# Gets the specified registry value or $Null if it is missing
[CmdletBinding()]
Param
(
[String] $path,
[String] $name,
[String] $ComputerName
)
If($ComputerName -eq $env:computername -or $ComputerName -eq "LocalHost")
{
$key = Get-Item -LiteralPath $path -EA 0
If($key)
{
Return $key.GetValue($name, $Null)
}
Else
{
Return $Null
}
}
# Path needed here is different for remote registry access
$path1 = $path.SubString( 6 )
$path2 = $path1.Replace( '\', '\\' )
$registry = $null
try
{
## Use the Remote Registry service
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
[Microsoft.Win32.RegistryHive]::LocalMachine,
$ComputerName )
}
catch
{
#$e = $error[ 0 ]
#3.06, remove the verbose message as it confised some people
#wv "Could not open registry on computer $ComputerName ($e)"
}
$val = $null
If( $registry )
{
$key = $registry.OpenSubKey( $path2 )
If( $key )
{
$val = $key.GetValue( $name )
$key.Close()
}
$registry.Close()
}
Return $val
}
Function GetBasicDCInfo {
Param
(
[Parameter( Mandatory = $true )]
[String] $dn # distinguishedName of a DC
)
$DCName = $dn.SubString( 0, $dn.IndexOf( '.' ) )
$SrvName = $dn.SubString( $dn.IndexOf( '.' ) + 1 )
$Results = Get-ADDomainController -Identity $DCName -Server $SrvName -EA 0
If($? -and $Null -ne $Results)
{
$GC = $Results.IsGlobalCatalog.ToString()
$ReadOnly = $Results.IsReadOnly.ToString()
$IPv4Address = $Results.IPv4Address -join ", "
$IPv6Address = $Results.IPv6Address -join ", "
$ServerOS = $Results.OperatingSystem
$tmp = Get-RegistryValue "HKLM:\software\microsoft\windows nt\currentversion" "installationtype" $DCName
If( $null -eq $tmp ) { $ServerCore = 'Unknown' }
ElseIf( $tmp -eq 'Server Core') { $ServerCore = 'Yes' }
Else { $ServerCore = 'No' }
}
Else
{
$GC = 'Unable to retrieve status'
$ReadOnly = $GC
$ServerOS = $GC
$ServerCore = $GC
$IPv4Address = $GC
$IPv6Address = $GC
}
$obj = [PSCustomObject] @{
DCName = $DCName
GC = $GC
ReadOnly = $ReadOnly
ServerOS = $ServerOS
ServerCore = $ServerCore
IPv4Address = $IPv4Address
IPv6Address = $IPv6Address
}
Return $obj
}
Function GetTimeServerRegistryKeys {
Param
(
[String] $DCName
)
$AnnounceFlags = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "AnnounceFlags" $DCName
If( $null -eq $AnnounceFlags )
{
## DCName can't be contacted or DCName is an appliance with no registry
$AnnounceFlags = 'n/a'
$MaxNegPhaseCorrection = 'n/a'
$MaxPosPhaseCorrection = 'n/a'
$NtpServer = 'n/a'
$NtpType = 'n/a'
$SpecialPollInterval = 'n/a'
$VMICTimeProviderEnabled = 'n/a'
$NTPSource = 'Cannot retrieve data from registry'
}
Else
{
$MaxNegPhaseCorrection = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "MaxNegPhaseCorrection" $DCName
$MaxPosPhaseCorrection = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config" "MaxPosPhaseCorrection" $DCName
$NtpServer = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" "NtpServer" $DCName
$NtpType = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" "Type" $DCName
$SpecialPollInterval = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient" "SpecialPollInterval" $DCName
$VMICTimeProviderEnabled = Get-RegistryValue "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\VMICTimeProvider" "Enabled" $DCName
$NTPSource = Invoke-Command -ComputerName $DCName {w32tm /query /computer:$DCName /source}
}
If( $VMICTimeProviderEnabled -eq 'n/a' )
{
$VMICEnabled = 'n/a'
}
ElseIf( $VMICTimeProviderEnabled -eq 0 )
{
$VMICEnabled = 'Disabled'
}
Else
{
$VMICEnabled = 'Enabled'
}
$obj = [PSCustomObject] @{
DCName = $DCName.Substring(0, $_.IndexOf( '.'))
TimeSource = $NTPSource
AnnounceFlags = $AnnounceFlags
MaxNegPhaseCorrection = $MaxNegPhaseCorrection
MaxPosPhaseCorrection = $MaxPosPhaseCorrection
NtpServer = $NtpServer
NtpType = $NtpType
SpecialPollInterval = $SpecialPollInterval
VMICTimeProvider = $VMICEnabled
}
Return $obj
}
function Get-WinADForestInformation {
$Data = @{ }
$ForestInformation = $(Get-ADForest)
$Data.Forest = $ForestInformation
$Data.RootDSE = $(Get-ADRootDSE -Properties *)
$Data.ForestName = $ForestInformation.Name
$Data.ForestNameDN = $Data.RootDSE.defaultNamingContext
$Data.Domains = $ForestInformation.Domains
$Data.ForestInformation = @{
'Forest Name' = $ForestInformation.Name
'Root Domain' = $ForestInformation.RootDomain
'Forest Functional Level' = $ForestInformation.ForestMode
'# of Domains' = ($ForestInformation.Domains).Count
'Sites Count' = ($ForestInformation.Sites).Count
'Forest Domains' = ($ForestInformation.Domains) -join ", "
'Sites' = ($ForestInformation.Sites) -join ", "
}
$Data.UPNSuffixes = Invoke-Command -ScriptBlock {
$UPNSuffixList = [PSCustomObject] @{
"Primary UPN" = $ForestInformation.RootDomain
"UPN Suffixes" = $ForestInformation.UPNSuffixes -join ","
}
return $UPNSuffixList
}
$Data.GlobalCatalogs = $ForestInformation.GlobalCatalogs
$Data.SPNSuffixes = $ForestInformation.SPNSuffixes
$Data.Sites = Invoke-Command -ScriptBlock {
$Sites = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites | Sort-Object
$SiteData = foreach ($Site in $Sites) {
[PSCustomObject] @{
"Site Name" = $site.Name
"Subnets" = ($site.Subnets | Sort-Object) -join ", "
"Servers" = ($Site.Servers) -join ", "
}
}
Return $SiteData
}
$Data.FSMO = Invoke-Command -ScriptBlock {
[PSCustomObject] @{
"Domain" = $ForestInformation.RootDomain
"Role" = 'Domain Naming Master'
"Holder" = $ForestInformation.DomainNamingMaster
}
[PSCustomObject] @{
"Domain" = $ForestInformation.RootDomain
"Role" = 'Schema Master'
"Holder" = $ForestInformation.SchemaMaster
}
foreach ($Domain in $ForestInformation.Domains) {
$DomainFSMO = Get-ADDomain $Domain | Select-Object PDCEmulator, RIDMaster, InfrastructureMaster
[PSCustomObject] @{
"Domain" = $Domain
"Role" = 'PDC Emulator'
"Holder" = $DomainFSMO.PDCEmulator
}
[PSCustomObject] @{
"Domain" = $Domain
"Role" = 'Infrastructure Master'
"Holder" = $DomainFSMO.InfrastructureMaster
}
[PSCustomObject] @{
"Domain" = $Domain
"Role" = 'RID Master'
"Holder" = $DomainFSMO.RIDMaster
}
}
Return $FSMO
}
$Data.OptionalFeatures = Invoke-Command -ScriptBlock {
$OptionalFeatures = $(Get-ADOptionalFeature -Filter * )
$Optional = @{
'Recycle Bin Enabled' = ''
'Privileged Access Management Feature Enabled' = ''
}
### Fix Optional Features
foreach ($Feature in $OptionalFeatures) {
if ($Feature.Name -eq 'Recycle Bin Feature') {
if ("$($Feature.EnabledScopes)" -eq '') {
$Optional.'Recycle Bin Enabled' = $False
}
else {
$Optional.'Recycle Bin Enabled' = $True
}
}
if ($Feature.Name -eq 'Privileged Access Management Feature') {
if ("$($Feature.EnabledScopes)" -eq '') {
$Optional.'Privileged Access Management Feature Enabled' = $False
}
else {
$Optional.'Privileged Access Management Feature Enabled' = $True
}
}
}
return $Optional
### Fix optional features
}
return $Data
}
$TableHeader = "<table style=`"width: 100%; border-collapse: collapse; border: 1px solid black;`">"
$Whitespace = "<br/>"
$TableStyling = "<th>", "<th align=`"left`" style=`"background-color:#00adef; border: 1px solid black;`">"
$RawAD = Get-WinADForestInformation
$ForestRawInfo = new-object PSCustomObject -property $RawAD.ForestInformation | convertto-html -Fragment | Select-Object -Skip 1
$ForestToc = "<div id=`"forest_summary`"></div>"
$ForestNice = $ForestToc + $TableHeader + ($ForestRawInfo -replace $TableStyling) + $Whitespace
$SiteRawInfo = $RawAD.Sites | Select-Object 'Site Name', Servers, Subnets | ConvertTo-Html -Fragment | Select-Object -Skip 1
$SiteHeader = "<p id=`"site_summary`"><i>AD Forest Physical Structure.</i></p>"
$SiteNice = $SiteHeader + $TableHeader + ($SiteRawInfo -replace $TableStyling) + $Whitespace
$DomainsRawInfo = $(Get-WinADForestInformation).Domains | ForEach-Object { Get-ADDomain $_ | Select-Object Name, NetBIOSName, DomainMode } | ConvertTo-Html -Fragment | Select-Object -Skip 1
$DomainsHeader = "<p id=`"domain_summary`"><i>AD Forest Logical Structure.</i></p>"
$DomainsNice = $DomainsHeader + $TableHeader + ($DomainsRawInfo -replace $TableStyling) + $Whitespace
$OptionalRawFeatures = new-object PSCustomObject -property $RawAD.OptionalFeatures | convertto-html -Fragment | Select-Object -Skip 1
$OptionalFeaturesToc = "<div id=`"optional_features`"></div>"
$OptionalNice = $OptionalFeaturesToc + $TableHeader + ($OptionalRawFeatures -replace $TableStyling) + $Whitespace
$UPNRawFeatures = $RawAD.UPNSuffixes | convertto-html -Fragment -as list| Select-Object -Skip 1
$UPNToc = "<div id=`"upn_suffixes`"></div>"
$UPNNice = $UPNToc + $TableHeader + ($UPNRawFeatures -replace $TableStyling) + $Whitespace
$DCRawFeatures = $RawAD.GlobalCatalogs| Sort-Object | ForEach-Object { GetBasicDCInfo $_ } | convertto-html -Fragment | Select-Object -Skip 1
$DCToc = "<div id=`"domain_controllers`"></div>"
$DCNice = $DCToc + $TableHeader + ($DCRawFeatures -replace $TableStyling) + $Whitespace
$DCRawNTPconfig = $RawAD.GlobalCatalogs | Sort-Object | ForEach-Object { (GetTimeServerRegistryKeys $_) } | convertto-html -Fragment | Select-Object -Skip 1
$NTPToc = "<div id=`"ntp_configuration`"></div>"
$DCNTPconfigNice = $NTPToc + $TableHeader + ($DCRawNTPconfig -replace $TableStyling) + $Whitespace
$FSMORawFeatures = $RawAD.FSMO | convertto-html -Fragment | Select-Object -Skip 1
$FSMOToc = "<div id=`"fsmo_roles`"></div>"
$FSMONice = $FSMOToc + $TableHeader + ($FSMORawFeatures -replace $TableStyling) + $Whitespace
#$ForestFunctionalLevel = $RawAD.RootDSE.forestFunctionality
#$DomainFunctionalLevel = $RawAD.RootDSE.domainFunctionality
#$domaincontrollerMaxLevel = $RawAD.RootDSE.domainControllerFunctionality
$passwordpolicyraw = Get-ADDefaultDomainPasswordPolicy | Select-Object ComplexityEnabled, PasswordHistoryCount, LockoutDuration, LockoutThreshold, MaxPasswordAge, MinPasswordAge | convertto-html -Fragment -As List | Select-Object -skip 1
$passwordpolicyheader = "<tr><th>Policy</th><th><b>Setting</b></th></tr>"
$passwordToc = "<div id=`"default_password_policies`"></div>"
$passwordpolicyNice = $passwordToc + $TableHeader + ($passwordpolicyheader -replace $TableStyling) + ($passwordpolicyraw -replace $TableStyling) + $Whitespace
$adminsraw = Get-ADGroupMember "Domain Admins" | Select-Object SamAccountName, Name | convertto-html -Fragment | Select-Object -Skip 1
$adminsToc = "<div id=`"domain_admins`"></div>"
$adminsnice = $adminsToc + $TableHeader + ($adminsraw -replace $TableStyling) + $Whitespace
$TotalUsers = (Get-AdUser -filter *).count
$EnabledUsers = (Get-AdUser -filter * | Where-Object { $_.enabled -eq $true }).count
$DisabledUSers = (Get-AdUser -filter * | Where-Object { $_.enabled -eq $false }).count
$DomainAdminUsers = (Get-ADGroupMember -Identity "Domain Admins").count
$EnterpriseAdminUsers = (Get-ADGroupMember -Identity "Enterprise Admins").count
$SchemaAdminUsers = (Get-ADGroupMember -Identity "Schema Admins").count
$AdminCountUsers = (Get-ADUser -LDAPFilter "(admincount=1)").count
$UsersCountObj = [PSCustomObject] @{
'Total' = $TotalUsers
'Enabled' = $EnabledUsers
'Disabled' = $DisabledUSers
'Domain Admins' = $DomainAdminUsers
'Enterprise Admins' = $EnterpriseAdminUsers
'Schema Admins' = $SchemaAdminUsers
'AdminCount users' = $AdminCountUsers
}
$userTotalsRaw = $UsersCountObj | convertto-html -Fragment | Select-Object -Skip 1
$userTotalsToc = "<div id=`"user_count`"></div>"
$userTotalsNice = $userTotalsToc + $TableHeader + ($userTotalsRaw -replace $TableStyling) + $Whitespace
$currentDate = Get-Date -Format "dddd dd/MM/yyyy HH:mm K"
$toc = '<h2><center>
<a href="#forest_summary">FOREST SUMMARY</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#site_summary">SITE SUMMARY</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#domain_summary">DOMAIN SUMMARY</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#domain_controllers">DOMAIN CONTROLLERS</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#ntp_configuration">NTP CONFIGURATION</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#fsmo_roles">FSMO ROLES</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#optional_features">OPTIONAL FEATURES</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#upn_suffixes">UPN SUFFIXES</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#default_password_policies">DEFAULT PASSWORD POLICIES</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#user_count">USER COUNT</a>&nbsp;&nbsp;|&nbsp;&nbsp;
<a href="#domain_admins">DOMAIN ADMINS</a>
</center></h2><br />'
# Setup the fields for the Asset
$AssetFields = @{
'last_updated' = $currentDate
'table_of_contents' = $toc
'forest_name' = $RawAD.ForestName
'forest_summary' = $ForestNice
'site_summary' = $SiteNice
'domain_summary' = $DomainsNice
'domain_controllers' = $DCNice
'ntp_configuration' = $DCNTPconfigNice
'fsmo_roles' = $FSMONice
'optional_features' = $OptionalNice
'upn_suffixes' = $UPNNice
'default_password_policies' = $passwordpolicyNice
'domain_admins' = $adminsnice
'user_count' = $userTotalsNice
}
# Checking if the FlexibleAsset exists. If not, create a new one.
$Layout = Get-HuduAssetLayouts -name $HuduAssetLayoutName
if (!$Layout) {
$AssetLayoutFields = @(
@{
label = 'Last Updated'
field_type = 'Text'
show_in_list = 'true'
position = 1
},
@{
label = 'Table of Contents'
field_type = 'RichText'
show_in_list = 'false'
position = 2
},
@{
label = 'Forest Name'
field_type = 'Text'
show_in_list = 'true'
position = 3
},
@{
label = 'Forest Summary'
field_type = 'RichText'
show_in_list = 'false'
position = 4
},
@{
label = 'Site Summary'
field_type = 'RichText'
show_in_list = 'false'
position = 5
},
@{
label = 'Domain Summary'
field_type = 'RichText'
show_in_list = 'false'
position = 6
},
@{
label = 'Domain Controllers'
field_type = 'RichText'
show_in_list = 'false'
position = 7
},
@{
label = 'NTP Configuration'
field_type = 'RichText'
show_in_list = 'false'
position = 8
},
@{
label = 'FSMO Roles'
field_type = 'RichText'
show_in_list = 'false'
position = 9
},
@{
label = 'Optional Features'
field_type = 'RichText'
show_in_list = 'false'
position = 10
},
@{
label = 'UPN Suffixes'
field_type = 'RichText'
show_in_list = 'false'
position = 11
},
@{
label = 'Default Password Policies'
field_type = 'RichText'
show_in_list = 'false'
position = 12
},
@{
label = 'User Count'
field_type = 'RichText'
show_in_list = 'false'
position = 13
},
@{
label = 'Domain Admins'
field_type = 'RichText'
show_in_list = 'false'
position = 14
}
)
Write-Host "Creating New Asset Layout"
#$NewLayout = New-HuduAssetLayout -name $HuduAssetLayoutName -icon "fas fa-sitemap" -color "#00adef" -icon_color "#000000" -include_passwords $false -include_photos $false -include_comments $false -include_files $false -fields $AssetLayoutFields
New-HuduAssetLayout -name $HuduAssetLayoutName -icon "fas fa-sitemap" -color "#00adef" -icon_color "#000000" -include_passwords $false -include_photos $false -include_comments $false -include_files $false -fields $AssetLayoutFields
$Layout = Get-HuduAssetLayouts -name $HuduAssetLayoutName
}
$Company = Get-HuduCompanies -name $CompanyName
if ($company) {
#Upload data to Hudu
$Asset = Get-HuduAssets -name $RawAD.ForestName -companyid $company.id -assetlayoutid $layout.id
#If the Asset does not exist, we edit the body to be in the form of a new asset, if not, we just upload.
if (!$Asset) {
Write-Host "Creating new Asset"
$Asset = New-HuduAsset -name $RawAD.ForestName -company_id $company.id -asset_layout_id $layout.id -fields $AssetFields
}
else {
Write-Host "Updating Asset"
$Asset = Set-HuduAsset -asset_id $Asset.id -name $RawAD.ForestName -company_id $company.id -asset_layout_id $layout.id -fields $AssetFields
}
} else {
Write-Host "$CompanyName was not found in Hudu"
}
+5 -5
View File
@@ -8,13 +8,13 @@ If (! (Test-Path "${Env:ProgramFiles}\4KDownload\4kvideodownloader\4kvideodownlo
$INSTALLER = Download-File -URL $URL
## Install the app
LogMsg "Installing 4K Video Downloader from ""${INSTALLER}"""
Write-Output "Installing 4K Video Downloader from ""${INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${INSTALLER} /qn /norestart" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Delete the installer
LogMsg "Deleting downloaded installer from ""${INSTALLER}"""
Write-Output "Deleting downloaded installer from ""${INSTALLER}"""
Try { Remove-Item -Path $INSTALLER -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
Else { LogMsg "4K Video Downloader is already installed!" }
Else { Write-Output "4K Video Downloader is already installed!" }
+3 -3
View File
@@ -3,8 +3,8 @@ $URL = 'https://download.adobe.com/pub/connect/updaters/meeting/11_0/ConnectApp1
## Install Adobe Connect
If ( IsURLValid $URL ) {
LogMsg "Installing Adobe Connect from ${URL}"
Write-Output "Installing Adobe Connect from ${URL}"
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${URL} /qn /norestart" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
Else { LogMsg "URL is not valid: ${URL}" }
Else { Write-Output "URL is not valid: ${URL}" }
-75
View File
@@ -1,75 +0,0 @@
param(
[Parameter(Mandatory=$false)][int]$CUSTOMER_ID=1,
[Parameter(Mandatory=$false)][string]$INTEGRATOR_ID,
[Parameter(Mandatory=$false)][string]$AGENT_FILENAME="AteraAgentSetup.msi",
[Parameter(Mandatory=$false)][switch]$FORCE
)
# Make sure $INTEGRATOR_ID exists, otherwise exit this script as an agent cannot be downloaded
#If ( ($INTEGRATOR_ID -eq $null) -or ($INTEGRATOR_ID -eq '') ) { Exit }
# Source the Tools.ps1 script
Function SourceTools
{ . $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1'))) }
. SourceTools
# Build the path for $AGENT_INSTALLER
$AGENT_INSTALLER = '{0}{1}' -f (GetTempPath), $AGENT_FILENAME
# Make sure we need to install the agent
If ( (Test-Path "${Env:ProgramFiles}\ATERA Networks\AteraAgent\AteraAgent.exe") -or (Test-Path "${Env:ProgramFiles(x86)}\ATERA Networks\AteraAgent\AteraAgent.exe") )
{ $INSTALL = $false ; $PreviousInstall = $true ; LogMsg "Atera agent is already installed on this endpoint" } Else { $INSTALL = $true ; $PreviousInstall = $false }
If ( $FORCE ) { $INSTALL = $true }
If ( $INSTALL ) {
# Uninstall if $FORCE is true
If ( $PreviousInstall ) {
LogMsg "Getting information from existing installation"
$ACCOUNT_ID = (Get-ItemPropertyValue -Path 'HKLM:SOFTWARE\ATERA Networks\AlphaAgent' -Name 'AccountId' -ErrorAction SilentlyContinue)
$AGENT_ID = (Get-ItemPropertyValue -Path 'HKLM:SOFTWARE\ATERA Networks\AlphaAgent' -Name 'AgentId' -ErrorAction SilentlyContinue)
$CUSTOMER_ID = (Get-ItemPropertyValue -Path 'HKLM:SOFTWARE\ATERA Networks\AlphaAgent' -Name 'CompanyId' -ErrorAction SilentlyContinue)
$INTEGRATOR_ID = (Get-ItemPropertyValue -Path 'HKLM:SOFTWARE\ATERA Networks\AlphaAgent' -Name 'IntegratorLogin' -ErrorAction SilentlyContinue)
LogMsg "AccountID : ${ACCOUNT_ID}"
LogMsg "AgentID : ${AGENT_ID}"
LogMsg "CustomerID : ${CUSTOMER_ID}"
LogMsg "IntegratorID: ${INTEGRATOR_ID}"
#Download and run the uninstall script
$UninstallScript = Download-File -URL 'https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Uninstall-AteraAgent.ps1'
Try { Start-Process "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -Noninteractive -File ""${UninstallScript}""" -Wait }
Catch { LogErr $_.Exception.Message }
}
# Replace the '@' with '%40' - not sure why this is necessary, but it is
$INTEGRATOR_ID = $INTEGRATOR_ID.Replace('@','%40')
# Download agent installer
Download-File -URL "https://app.atera.com/GetAgent/Msi/?customerId=${CUSTOMER_ID}&integratorLogin=${INTEGRATOR_ID}" -File "${AGENT_INSTALLER}"
# Install the agent
LogMsg "Installing Atera agent from ""${AGENT_INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ""${AGENT_INSTALLER}"" /qn /norestart" -Wait }
Catch { LogErr $_.Exception.Message }
# Delete the agent installer
LogMsg "Deleting ""${AGENT_INSTALLER}"""
Try { Remove-Item $AGENT_INSTALLER -Force }
Catch { LogErr $_.Exception.Message }
If ( $PreviousInstall -and $ACCOUNT_ID -and $AGENT_ID -and $CUSTOMER_ID) {
LogMsg "Restoring agent information from previous install"
LogMsg " - Stopping AteraAgent service"
Stop-Service -Name 'AteraAgent' -Force
LogMsg " - Restoring AgentID and AccountID from previous install"
Set-ItemProperty -Path 'HKLM:SOFTWARE\ATERA Networks\AlphaAgent' -Name 'AccountId' -Value $ACCOUNT_ID
Set-ItemProperty -Path 'HKLM:SOFTWARE\ATERA Networks\AlphaAgent' -Name 'AgentId' -Value $AGENT_ID
LogMsg " - Starting AteraAgent service"
Start-Service -Name 'AteraAgent'
}
}
# Delete this script
Remove-Item $MyInvocation.MyCommand.Path -Force
+8 -12
View File
@@ -1,15 +1,11 @@
## Define URL for downloading the installer
$URL = 'https://efulfillment.autodesk.com/NetSWDLD/2020/DTCONN/2FF3C1A4-14EC-3C6D-B127-47327638CC14/SFX/Autodesk_Desktop_Connector_15_5_0_1684_Win_64bit.sfx.exe'
# Define URL for downloading the installer
$URL = Get-AbsoluteURI -URL 'https://www.autodesk.com/adsk-connect-64?'
## Make sure the URL is valid before we do anything
If ( IsURLValid $URL )
{
## Set the local file name to download to
$FILE = '{0}{1}' -f (GetTempPath),(Split-Path $URL -Leaf)
# Set the local file name to download to
$FILE = '{0}{1}' -f (Get-TempPath),(Split-Path $URL -Leaf)
## Download installer
Download-File -URL $URL -File $FILE
# Download installer
Download-File -URL $URL -File $FILE
## Install package
Start-Process $FILE
}
# Install package
Start-Process $FILE
+5 -5
View File
@@ -1,7 +1,7 @@
$SHORTCUT_PATH = "${Env:Public}\Desktop\Deltek Vision.lnk"
# Remove the shortcut if it already exists
If ( Test-Path $SHORTCUT_PATH ) { LogMsg "Removing existing shortcut: ${SHORTCUT_PATH}" ; Remove-Item $SHORTCUT_PATH }
If ( Test-Path $SHORTCUT_PATH ) { Write-Output "Removing existing shortcut: ${SHORTCUT_PATH}" ; Remove-Item $SHORTCUT_PATH }
# If the shortcut doesn't exist
If ( -not (Test-Path $SHORTCUT_PATH) ) {
@@ -28,10 +28,10 @@ If ( -not (Test-Path $SHORTCUT_PATH) ) {
## Create the shortcut
If ( $BROWSER_PATH ) {
LogMsg "Installing Deltek Vision for ${APP_NAME}"
Create-Shortcut -Path "${Env:Public}\Desktop\Deltek Vision.lnk" -TargetPath $BROWSER_PATH -Arguments $VisionURL -Description "Launch Deltek Vision"
Write-Output "Installing Deltek Vision for ${APP_NAME}"
New-Shortcut -Path "${Env:Public}\Desktop\Deltek Vision.lnk" -TargetPath $BROWSER_PATH -Arguments $VisionURL -Description "Launch Deltek Vision"
}
Else { LogMsg "A compatible browser could not be found: ${BROWSER_PATH}" }
Else { Write-Output "A compatible browser could not be found: ${BROWSER_PATH}" }
}
Else { LogMsg "No URL to Deltek Vision was specified!" }
Else { Write-Output "No URL to Deltek Vision was specified!" }
}
+1 -1
View File
@@ -14,7 +14,7 @@ ElseIf ( (IsWindowsVersion -ge "6.1") -and (IsWindowsVersion -lt "10.0") )
If ( Is64bit ) { $URL = '{0}/{1}' -f $BaseURL, 'DisplayLink_Win7-8.1_x64.msi' }
Else { $URL = '{0}/{1}' -f $BaseURL, 'DisplayLink_Win7-8.1_x86.msi' }
}
Else { LogMsg "Operating system is not supported" ; Exit }
Else { Write-Output "Operating system is not supported" ; Exit }
## Install DisplayLink
Install-Program -Path "msiexec.exe" -Arguments "/i ${URL} /qn /norestart"
+23 -11
View File
@@ -1,23 +1,35 @@
# NOTE: The $CONFIG variable must be set by the calling script
# Check whether agent is already installed
If ( Test-Path "${Env:ProgramFiles}\ESET\RemoteAdministrator\Agent\ERAAgent.exe" ) {
# End the script if no errors are found in the status log
If ( !(Select-String -Path "${Env:ProgramData}\ESET\RemoteAdministrator\Agent\EraAgentApplicationData\Logs\status.html" -CaseSensitive -Pattern 'Error:' -Quiet) ) {
Microsoft.Powershell.Utility\Write-Output "ESET agent is already installed and no errors were found. This script will now exit."
Return
}
Write-Output "ESET agent is already installed, but errors were found in the status log. This script will continue and attempt to reinstall the agent."
}
# Set path to temp directory
$TEMPDIR = '{0}eset-protect-deployment' -f (GetTempPath)
$TEMPDIR = '{0}eset-protect-deployment' -f (Get-TempPath)
# Recursively delete $TEMPDIR if it already exists
If ( Test-Path $TEMPDIR ) {
LogMsg """${TEMPDIR}"" already exists and will be deleted"
Write-Output """${TEMPDIR}"" already exists and will be deleted"
Try { Remove-Item -Path $TEMPDIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
# Create $TEMPDIR
LogMsg "Creating: ${TEMPDIR}"
Write-Output "Creating: ${TEMPDIR}"
Try { New-Item -Path $TEMPDIR -ItemType Directory -Force -ErrorAction Stop }
Catch { LogErr $_.Exception.Message ; Exit }
Catch { Write-Error $_.Exception.Message ; Return }
# Write $CONFIG to install_config.ini
Try { $CONFIG | WriteToFile_UTF8NoBOM -FilePath "${TEMPDIR}\install_config.ini" }
Catch { LogMsg "Cannot write configuration file! This script will exit." ; LogErr $_.Exception.Message ; Exit }
Catch { Write-Output "Cannot write configuration file! This script will exit." ; Write-Error $_.Exception.Message ; Return }
# Get bitness of endpoint and set $AGENTNAME
If ( Is64bit ) { $AGENTNAME = "agent_x64.msi" } Else { $AGENTNAME = "agent_x86.msi" }
@@ -25,15 +37,15 @@ If ( Is64bit ) { $AGENTNAME = "agent_x64.msi" } Else { $AGENTNAME = "agent_x86.m
# Download the installer
$INSTALLER = "${TEMPDIR}\${AGENTNAME}"
$URL = 'https://download.eset.com/com/eset/apps/business/era/agent/latest/{0}' -f $AGENTNAME
LogMsg "Downloading ESET Management Agent"
Write-Output "Downloading ESET Management Agent"
Download-File -URL $URL -File $INSTALLER
# Install the app
LogMsg "Installing ESET Management Agent from ""${INSTALLER}"""
Write-Output "Installing ESET Management Agent from ""${INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${INSTALLER} /qn /norestart /log installer.log" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
# Delete the installer
LogMsg "Deleting temp directory and its contents"
Write-Output "Deleting temp directory and its contents"
Try { Remove-Item -Path $TEMPDIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
+65 -30
View File
@@ -1,13 +1,10 @@
## This script requires the $Edition variable is set from the RMM script
If (! $Edition ) { $Edition = 'desktop' }
# Specify URL to "Backup for Windows" agent installer
$ECB_WIN_URL = 'https://console.msp360.com/api/build/download?urlParams=OEFsMzhldm1SbjhaSDhBc2VtYkpUREM3Y2pJSzg0REU4dkVtR1pHREkxMGFFUk9qZXJnSXk5SUF0ei8wZlZOWTBzNG1zVDV0ckVMdStZVzBsam5uZklMVnZLeTBMVmxGNWt4VDhSMzNIL3VmK3VHNmxBVEZTQkxhMFhNR2ZuZzJCeE80MDJxVkRSWXdNa0dqSHdpbUN1bE01c3BWNFBVUmt2Q1c4endncjVZemFSSjEyYmFNL3hESHR2eWpVYXpBVlV2eU1kZVZFaEpvalk2Rk40Wk96UG9QdUwxYnZrRHREMmhLeTZoMW5Rbz0*&brandId=fb8308e2-448a-4645-a5f6-a3632a7e57f4'
## Specify URL to "Backup for Windows" agent installer
$ECB_WIN_URL = 'https://s3.amazonaws.com/cb_setups/MBS/53CFB286-7A97-4FDF-8CFA-475C0DB63A9A/Brands/FB8308E2-448A-4645-A5F6-A3632A7E57F4/EmberkomCloudBackup_v7.7.1.40_ALLEDITIONS_Setup.exe'
# Specify URL to "Backup Virtual Machine Edition" agent installer
$ECB_VMM_URL = 'https://console.msp360.com/api/build/download?urlParams=OEFsMzhldm1SbjhaSDhBc2VtYkpUREM3Y2pJSzg0REU4dkVtR1pHREkxMGFFUk9qZXJnSXk5SUF0ei8wZlZOWUpYM2xDY3ErMkpOVGpuWTRISDNFcFRFdWRibDgvTm8vUDhpWndON3kzaHNwOTFVa3h6WUdoeVVod1JmT2lRQ1FlVTE3Y0pnUncwSEJwS3FFcDBmTWt3cEdnSy9YemRsSTRiQWJHNi96cUtWbzZsc25QVllaR1QyQ21VVS95dFFVTkNndXdPVStGQXBIT0FZb2FIUzFuZy9mMU5qZUd5emhab1hFYjZWODB4ND0*&brandId=fb8308e2-448a-4645-a5f6-a3632a7e57f4'
## Specify URL to "Backup Virtual Machine Edition" agent installer
$ECB_VMM_URL = 'https://s3.amazonaws.com/cb_setups/MBS/53CFB286-7A97-4FDF-8CFA-475C0DB63A9A/Brands/FB8308E2-448A-4645-A5F6-A3632A7E57F4/EmberkomCloudBackup_v7.7.1.40_VMWARE_Setup.exe'
## Set the correct download URL and agent edition
# Set the correct download URL and agent edition
switch ( $Edition.ToLower() ) {
'server' { $URL = $ECB_WIN_URL; $AgentEdition = 'baremetal' }
'desktop' { $URL = $ECB_WIN_URL; $AgentEdition = 'desktop' }
@@ -15,29 +12,67 @@ switch ( $Edition.ToLower() ) {
default { $URL = $ECB_WIN_URL; $AgentEdition = 'desktop' }
}
## Install and import additional Powershell module
Install-MSP360Module
Import-Module -Name MSP360
# Install and import additional Powershell module
Import-MSP360Module
## Install the agent
LogMsg "Installing Emberkom Cloud Backup agent"
Try { Install-MBSAgent -URL $URL -Force }
Catch { LogErr $_.Exception.Message }
## Add backup user to installed product
Try {
$MSP360Password = ConvertTo-SecureString -string $MSP360Password -AsPlainText -Force
LogMsg "Adding backup user account: ${MSP360Username}"
Add-MBSUserAccount -User $MSP360Username -Password $MSP360Password
# Exit this script if the agent is already installed
If ( $(Get-MBSAgent -ErrorAction SilentlyContinue) ) {
Microsoft.Powershell.Utility\Write-Output "Backup agent is already installed. This script will now exit."
Return
}
Catch { LogErr $_.Exception.Message }
## Set the agent edition
## Note: the product edition must be set *after* the user is added
LogMsg "Setting Emberkom Cloud Backup agent edition to ""$AgentEdition"""
Try { Set-MBSAgentSetting -Edition $AgentEdition -Verbose }
Catch { LogErr $_.Exception.Message }
#
# The URL is now too long to pass as a variable to other functions and cmdlets, so this is a workaround to download the file
#
## Delete the desktop icon
"${Env:PUBLIC}\Desktop\Emberkom Backup.LNK",
"${Env:PUBLIC}\Desktop\Emberkom Cloud Backup.LNK" | Remove-File
# Make sure the URL is valid
$StatusCode = [System.Net.WebRequest]::Create($URL).GetResponse().StatusCode
If ( !(($StatusCode -ge 200) -and ($StatusCode -lt 400)) ) { Write-Output "URL cannot be reached (status code ${StatusCode}): ${URL}" ; Return }
# Make a local file to download to
$Installer = '{0}EmberkomCloudBackup.exe' -f (Get-TempPath)
# Delete previous local file if it exists
If ( Test-Path $Installer ) { Write-Output "Deleting previously downloaded file: ${Installer}" ; Remove-Item $Installer -Force -ErrorAction SilentlyContinue }
# Download the file
Try { (New-Object System.Net.WebClient).DownloadFile($URL,$Installer) }
Catch { Write-Error $_.Exception.Message ; Remove-Item $Installer -Force -ErrorAction SilentlyContinue }
# Make sure the file exists
If ( !(Test-Path $Installer) ) { Write-Output "There was a problem downloading the file, this script will not continue" ; Return }
#
# End the workaround
#
# Install the agent
Write-Output "Installing Emberkom Cloud Backup agent"
Try { Start-Process "${Installer}" -ArgumentList "/S" -Wait }
Catch { Write-Error $_.Exception.Message }
# Add backup user to installed product
Write-Output "Adding backup user account: ${MSP360Username}"
Try { Add-MBSUserAccount -User $MSP360Username -Password $(ConvertTo-SecureString -string $MSP360Password -AsPlainText -Force) }
Catch { Write-Error $_.Exception.Message }
# Wait a few seconds to let the agent check-in
Start-Sleep -Seconds 10
# Set the agent edition
# Note: the product edition must be set *after* the user is added
Write-Output "Setting Emberkom Cloud Backup agent edition to ""$AgentEdition"""
Try {
Switch ([string]::IsNullOrEmpty($MasterPassword)) {
$true { Set-MBSAgentSetting -Edition $AgentEdition -Verbose }
$false { Set-MBSAgentSetting -Edition $AgentEdition -MasterPassword $(ConvertTo-SecureString -String $MasterPassword -AsPlainText -Force) -Verbose }
}
}
Catch { Write-Error $_.Exception.Message }
# Delete $Installer
If ( Test-Path $Installer ) { Write-Output "Deleting downloaded file: ${Installer}" ; Remove-Item $Installer -Force -ErrorAction SilentlyContinue }
# Delete the desktop icon
If ( Test-Path "${Env:PUBLIC}\Desktop\Emberkom Backup.LNK" ) { Remove-Item -Path "${Env:PUBLIC}\Desktop\Emberkom Backup.LNK" -Force -ErrorAction SilentlyContinue }
If ( Test-Path "${Env:PUBLIC}\Desktop\Emberkom Cloud Backup.LNK" ) { Remove-Item -Path "${Env:PUBLIC}\Desktop\Emberkom Cloud Backup.LNK" -Force -ErrorAction SilentlyContinue }
+55 -12
View File
@@ -1,15 +1,58 @@
## Path to the MSI installer
$MSIURL = 'https://enscape-installer.chaosgroup.com/installer.enscape.io/Enscape-3.4.1%2B87719.msi'
# Exit early if apps are running
$ExitEarly = $false
$AppNames = @()
If ( IsRunning -Name 'revit' ) { $ExitEarly = $true ; $AppNames.Add('Revit') }
If ( IsRunning -Name 'sketchup' ) { $ExitEarly = $true ; $AppNames.Add('SketchUp') }
If ( IsRunning -Name 'archicad' ) { $ExitEarly = $true ; $AppNames.Add('ArchiCAD') }
If ( IsRunning -Name 'autocad' ) { $ExitEarly = $true ; $AppNames.Add('AutoCAD') }
If ( IsRunning -Name 'rhino' ) { $ExitEarly = $true ; $AppNames.Add('Rhino') }
If ( IsRunning -Name 'vectorworks' ) { $ExitEarly = $true ; $AppNames.Add('VectorWorks') }
If ( $ExitEarly ) { Write-Output "Installation cannot continue because the following apps are running: ${AppNames}." ; Return }
## Run the installer
$INSTALLER = Download-File -URL $MSIURL
LogMsg "Installing Enscape from ""${INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${INSTALLER} /qn /norestart" -Wait -ErrorAction Stop }
Catch { LogErr $_.Exception.Message }
# Path to the MSI installer
$URL = 'https://enscape-installer.chaosgroup.com/installer.enscape.io/Enscape-4.0.2.11.exe'
## Delete installer
Try {
LogMsg "Deleting downloaded installation package"
Remove-Item $INSTALLER -Force -ErrorAction SilentlyContinue
# Set path to temp directory
$TEMPDIR = '{0}enscape-installer' -f (Get-TempPath)
# Recursively delete $TEMPDIR if it already exists
If ( Test-Path $TEMPDIR ) {
Write-Output """${TEMPDIR}"" already exists and will be deleted"
Try { Remove-Item -Path $TEMPDIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
Catch { LogErr $_.Exception.Message }
# Create $TEMPDIR
Write-Output "Creating: ${TEMPDIR}"
Try { New-Item -Path $TEMPDIR -ItemType Directory -Force -ErrorAction Stop }
Catch { Write-Error $_.Exception.Message ; Return }
# Write $CONFIG to config.xml
$CONFIG = @'
<DefValues>
<Value Name="INSTALL_SKETCHUP" DataType="value">1</Value>
<Value Name="STDROOT" DataType="value">C:\Program Files\Enscape</Value>
<Value Name="INSTALL_RHINO" DataType="value">1</Value>
<Value Name="INSTALL_ARCHICAD" DataType="value">1</Value>
<Value Name="LOCALE" DataType="value">en-US</Value>
<Value Name="INSTALL_REVIT" DataType="value">1</Value>
<Value Name="INSTALL_VECTORWORKS" DataType="value">1</Value>
</DefValues>
'@
Try { $CONFIG | WriteToFile_UTF8NoBOM -FilePath "${TEMPDIR}\config.xml" }
Catch { Write-Output "Cannot write configuration file! This script will exit." ; Write-Error $_.Exception.Message ; Return }
# Download the installer
$Installer = "${TEMPDIR}\$(Split-Path $URL -Leaf)"
Download-File -URL $URL -File $Installer
# Install package
If ( !(Test-Path $Installer) ) { Write-Output "Cannot download installer! This script will exit." ; Return }
Write-Output "Installing Enscape from ""${Installer}"""
Start-Process $Installer -ArgumentList "-gui=0 -acceptEULA -configFile=""${TEMPDIR}\config.xml"" -quiet=1 -ignoreErrors=1" -Wait
# Delete the installer
Write-Output "Deleting temp directory and its contents"
Try { Remove-Item -Path $TEMPDIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
+20 -17
View File
@@ -1,34 +1,37 @@
## Path to the MSI installer
$MSIURL = 'https://lfupdate.s3.amazonaws.com/clients/outlook/admin_msi/LiquidFiles_Admin_2.0.132.msi'
# Path to the MSI installer
$MSIURL = 'https://lfupdate.s3.amazonaws.com/clients/outlook/admin_msi/LiquidFiles_OutlookPlugin_Admin_3.0.37.msi'
## Set installer args
# Set installer args
If (-not [string]::IsNullOrEmpty($LFHost) ) {
LogMsg "LiquidFiles target server: ${LFHost}"
Write-Output "LiquidFiles target server: ${LFHost}"
$INSTALLER_ARGS = "REGKEYSCRIPT=""BaseUrl=${LFHost};InjectPositionDefault=3"""
}
Else { $INSTALLER_ARGS = '' }
## Stop any running LiquidFilesWindowsAgent process
# Stop any running LiquidFilesWindowsAgent process
$RunningProcess = Get-Process -Name "LiquidFilesWindowsAgent"
If ($RunningProcess) {
LogMsg "Stopping the LiquidFiles agent"
Write-Output "Stopping the LiquidFiles agent"
$RunningProcess | Stop-Process -Force
}
## Run the installer
$INSTALLER = Download-File -URL $MSIURL
LogMsg "Installing Liquid Files Outlook Agent from ""${INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${INSTALLER} /qn /norestart ${INSTALLER_ARGS}" -Wait -ErrorAction Stop }
Catch { LogErr $_.Exception.Message }
# Uninstall existing LiquidFiles agent/plugin
'LiquidFiles*' | Uninstall-MSI
## Delete installer
# Run the installer
$INSTALLER = Download-File -URL $MSIURL
Write-Output "Installing Liquid Files Outlook Agent from ""${INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${INSTALLER} /qn /norestart ${INSTALLER_ARGS}" -Wait -ErrorAction Stop }
Catch { Write-Error $_.Exception.Message }
# Delete installer
Try {
LogMsg "Deleting downloaded installation package"
Write-Output "Deleting downloaded installation package"
Remove-Item $INSTALLER -Force -ErrorAction SilentlyContinue
}
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Start the agent
# Start the agent
$PathToAgentExe64 = "${Env:ProgramFiles(x86)}\LiquidFiles Windows Agent\LiquidFilesWindowsAgent.exe"
$PathToAgentExe32 = "${Env:ProgramFiles}\LiquidFiles Windows Agent\LiquidFilesWindowsAgent.exe"
If ( Test-Path $PathToAgentExe64 ) {
@@ -37,7 +40,7 @@ If ( Test-Path $PathToAgentExe64 ) {
$PathToAgentExe = $PathToAgentExe32
}
If ($PathToAgentExe) {
LogMsg "Starting LiquidFiles agent"
Write-Output "Starting LiquidFiles agent"
Try { Start-Process $PathToAgentExe }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
-31
View File
@@ -1,31 +0,0 @@
param(
[Parameter(Mandatory=$true)][string]$TOKEN,
[Parameter(Mandatory=$true)][int]$CUSTOMER_ID,
[Parameter(Mandatory=$false)][int]$SOFTWARE_ID=101,
[Parameter(Mandatory=$false)][string]$SERVER='manage.emberkom.com',
[Parameter(Mandatory=$false)][string]$AGENT_FILENAME='EmberkomAgentSetup.exe'
)
## Source the Tools.ps1 script
Function SourceTools
{ . $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1'))) }
. SourceTools
## Build the path for $AGENT_INSTALLER
$AGENT_INSTALLER = '{0}{1}' -f (GetTempPath), $AGENT_FILENAME
## Get the right path to agent.exe based on the system bit-level
If ( Is64bit ) { $AGENT_EXE = "${Env:ProgramFiles(x86)}\N-able Technologies\Windows Agent\bin\agent.exe" }
Else { $AGENT_EXE = "${Env:ProgramFiles}\N-able Technologies\Windows Agent\bin\agent.exe" }
## Make sure we need to install the agent
If ( !(Test-Path $AGENT_EXE) )
{
## Download agent and get path
$AGENT_INSTALLER = Download-File -URL 'https://manage.emberkom.com/download/2020.1.5.425/winnt/N-central/WindowsAgentSetup.exe' -File $AGENT_INSTALLER
## Install the agent
Install-Program $AGENT_INSTALLER -Arguments "/s /v"" /qn CUSTOMERID=${CUSTOMER_ID} CUSTOMERSPECIFIC=1 REGISTRATION_TOKEN=${TOKEN} SERVERPROTOCOL=HTTPS SERVERADDRESS=${SERVER} SERVERPORT=443""" -Delete
} Else { LogMsg "N-Central agent is already installed on this endpoint." }
+70
View File
@@ -0,0 +1,70 @@
# $COMPANY must be supplied by RMM
$PRODUCT = 'O365ProPlusRetail'
$BITNESS = '64'
$MIGARCH = 'TRUE'
$CHANNEL = 'SemiAnnual'
$UPDATES = 'TRUE'
$DISPLAY = 'None' # "None", "Full"
$ACCEULA = 'TRUE'
# Make changes if the system is 32bit
If ( !(Is64Bit) ) { $BITNESS = '32'; $MIGARCH = 'FALSE' }
$CONFIG = @"
<Configuration ID="e59fdca7-843c-4bee-8edf-33b4f8fb750f">
<Add OfficeClientEdition="${BITNESS}" Channel="${CHANNEL}" MigrateArch="${MIGARCH}">
<Product ID="${PRODUCT}">
<Language ID="en-us" />
<ExcludeApp ID="Groove" />
<ExcludeApp ID="Lync" />
</Product>
</Add>
<Property Name="SharedComputerLicensing" Value="0" />
<Property Name="FORCEAPPSHUTDOWN" Value="TRUE" />
<Property Name="DeviceBasedLicensing" Value="0" />
<Property Name="SCLCacheOverride" Value="0" />
<Updates Enabled="${UPDATES}" Channel="${CHANNEL}" />
<RemoveMSI />
<Display Level="${DISPLAY}" AcceptEULA="${ACCEULA}" />
<AppSettings>
<Setup Name="Company" Value="${COMPANY}" />
</AppSettings>
</Configuration>
"@
# Set path to temp directory
$TEMPDIR = '{0}microsoft-office-deployment' -f (Get-TempPath)
# Recursively delete $TEMPDIR if it already exists
If ( Test-Path $TEMPDIR ) {
Write-Output """${TEMPDIR}"" already exists and will be deleted"
Try { Remove-Item -Path $TEMPDIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
}
# Create $TEMPDIR
Write-Output "Creating: ${TEMPDIR}"
Try { New-Item -Path $TEMPDIR -ItemType Directory -Force -ErrorAction Stop }
Catch { Write-Error $_.Exception.Message ; Return }
# Write $CONFIG to config.xml
$CONFIGFILE = "${TEMPDIR}\config.xml"
Try { $CONFIG | WriteToFile_UTF8NoBOM -FilePath "${CONFIGFILE}" }
Catch { Write-Output "Cannot write configuration file! This script will exit." ; Write-Error $_.Exception.Message ; Return }
# Set the name of ODT setup utility
$ODT = "${TEMPDIR}\setup.exe"
# Download ODT
Write-Output "Downloading Office Deployment Tool"
Download-File -URL 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/setup-exe/download/setup.exe' -File $ODT
# Install Office
Write-Output "Installing Microsoft Office"
Try { Start-Process $ODT -ArgumentList "/configure ""${CONFIGFILE}""" -Wait }
Catch { Write-Error $_.Exception.Message }
# Cleanup
Write-Output "Deleting temp directory and its contents"
Try { Remove-Item -Path $TEMPDIR -Recurse -Force -ErrorAction SilentlyContinue }
Catch { Write-Error $_.Exception.Message }
+2 -2
View File
@@ -16,7 +16,7 @@ Install-Program -Path $(Download-File -URL $URL) -Arguments "/S" -Delete
## Start the app after installation
If ( Test-Path $InstallPath )
{
LogMsg "Starting Nextcloud"
Write-Output "Starting Nextcloud"
Try { Start-Process $InstallPath }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
+3 -3
View File
@@ -19,9 +19,9 @@ If ( IsAdmin )
Else { $Shortcut = "${Env:SYSTEMDRIVE}\Users\Public\Desktop\OpenVPN GUI.LNK" }
If ( Test-Path $Shortcut )
{
LogMsg "Deleting desktop shortcut: ${Shortcut}"
Write-Output "Deleting desktop shortcut: ${Shortcut}"
Try { Remove-Item $Shortcut -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
}
Else { LogMsg "Must be an administrator to install OpenVPN" }
Else { Write-Output "Must be an administrator to install OpenVPN" }
+5 -5
View File
@@ -1,4 +1,4 @@
If ( Test-Path "${Env:ProgramFiles}\simpleinout-desktop\Simple In Out.exe" ) { LogMsg "Simple In/Out is already installed." ; Exit }
If ( Test-Path "${Env:ProgramFiles}\simpleinout-desktop\Simple In Out.exe" ) { Write-Output "Simple In/Out is already installed." ; Exit }
# Specify the URL to download the app from
$URL = 'https://downloads.simpleinout.com/desktop/Simple-In-Out.msi'
@@ -7,11 +7,11 @@ $URL = 'https://downloads.simpleinout.com/desktop/Simple-In-Out.msi'
$INSTALLER = Download-File -URL $URL
# Install the app
LogMsg "Installing latest version of Simple In/Out from ""${INSTALLER}"""
Write-Output "Installing latest version of Simple In/Out from ""${INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${INSTALLER} /qn /norestart ALLUSERS=1" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
# Delete the installer
LogMsg "Deleting downloaded installer from ""${INSTALLER}"""
Write-Output "Deleting downloaded installer from ""${INSTALLER}"""
Try { Remove-Item -Path $INSTALLER -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
+15 -62
View File
@@ -1,67 +1,20 @@
## Make sure we have an administrative token
If ( IsAdmin )
{
## Define URL for downloading the installer
$URL = "https://www.sketchup.com/sketchup/${InstallVersion}/SketchUpPro-exe"
## Make sure the URL is valid before we do anything
If ( IsURLValid $URL )
{
## Uninstall SketchUp 2016
If ( Test-Path "${Env:ProgramFiles}\SketchUp\SketchUp 2016\SketchUp.exe" )
{
LogMsg "Uninstalling SketchUp 2016"
Try { Start-Process "msiexec.exe" -ArgumentList '/X{D87EE6DC-32BA-4219-AC75-0A6FD54ED058} /qn /norestart' -Wait }
Catch { LogErr $_.Exception.Message }
}
## Uninstall SketchUp 2017
If ( Test-Path "${Env:ProgramFiles}\SketchUp\SketchUp 2017\SketchUp.exe" )
{
LogMsg "Uninstalling SketchUp 2017"
Try { Start-Process "msiexec.exe" -ArgumentList '/X{E59BD84C-169B-4F3F-AC5D-85127CF67051} /qn /norestart' -Wait }
Catch { LogErr $_.Exception.Message }
}
## Uninstall SketchUp 2019
If ( Test-Path "${Env:ProgramFiles}\SketchUp\SketchUp 2019\SketchUp.exe" )
{
LogMsg "Uninstalling SketchUp 2019"
Try { Start-Process "msiexec.exe" -ArgumentList '/X{06964675-EB01-6D18-6704-429DE73A8319} /qn /norestart' -Wait }
Catch { LogErr $_.Exception.Message }
}
## Uninstall SketchUp 2020
If ( Test-Path "${Env:ProgramFiles}\SketchUp\SketchUp 2020\SketchUp.exe" )
{
LogMsg "Uninstalling SketchUp 2020"
Try { Start-Process "msiexec.exe" -ArgumentList '/X{5778f9a3-781e-16f1-a6bf-08fd59dfa77b} /qn /norestart' -Wait }
Catch { LogErr $_.Exception.Message }
}
## Uninstall SketchUp 2021
If ( Test-Path "${Env:ProgramFiles}\SketchUp\SketchUp 2021\SketchUp.exe" )
{
LogMsg "Uninstalling SketchUp 2021"
Try { Start-Process "msiexec.exe" -ArgumentList '/X{e31a06f0-fc24-f59f-5b2c-941521be9626} /qn /norestart' -Wait }
Catch { LogErr $_.Exception.Message }
}
# This is the URL for retreving the latest version of the specified installer
# Trying to query this URL directly no longer works now that CloudFlare is blocking script access
# It does, however, work in the browser to retrieve the direct download link
# https://www.sketchup.com/sketchup/2023/SketchUpPro-exe
## Uninstall SketchUp 2022
If ( Test-Path "${Env:ProgramFiles}\SketchUp\SketchUp 2022\SketchUp.exe" )
{
LogMsg "Uninstalling SketchUp 2022"
Try { Start-Process "msiexec.exe" -ArgumentList '/X{898ed298-4bc7-f67e-2e5b-6202a980787a} /qn /norestart' -Wait }
Catch { LogErr $_.Exception.Message }
}
# Define URL for downloading the installer
$URL = 'https://download.sketchup.com/SketchUpStudio-2023-1-340-117.exe'
## Set the local file name to download to
$FILE = '{0}{1}.exe' -f (GetTempPath),(Split-Path $URL -Leaf)
# TODO: Uninstall all versions of SketchUp
#'SketchUp 2019', 'SketchUp 2020', 'SketchUp 2021', 'SketchUp 2022', 'SketchUp 2023', 'SketchUp Viewer' | Uninstall-MSI
## Download installer
$FILE = Download-File -URL $URL -File $FILE
# Set the local file name to download to
$FILE = '{0}{1}' -f (Get-TempPath),(Split-Path $URL -Leaf)
## Install package
Install-Program -Path $FILE -Arguments "/silent" -Delete
}
}
# Download installer
$FILE = Download-File -URL $URL -File $FILE
# Install package
Install-Program -Path $FILE -Arguments "/silent" -Delete
+4 -4
View File
@@ -5,11 +5,11 @@ $URL = 'https://specsintact.ksc.nasa.gov/Software/downloads/SpecsIntact.msi'
$INSTALLER = Download-File -URL $URL
## Install the app
LogMsg "Installing latest version of SpecsIntact from ""${INSTALLER}"""
Write-Output "Installing latest version of SpecsIntact from ""${INSTALLER}"""
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${INSTALLER} /qn /norestart" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Delete the installer
LogMsg "Deleting downloaded installer from ""${INSTALLER}"""
Write-Output "Deleting downloaded installer from ""${INSTALLER}"""
Try { Remove-Item -Path $INSTALLER -Force -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
+31
View File
@@ -0,0 +1,31 @@
# This script is intended to be called from a Windows Provisioning Package.
# The provisioning package invokes this script the following way:
# powershell.exe -ExecutionPolicy Bypass -Command ". $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Install-SyncroAgent.ps1'))) ; Install-SyncroAgent -URL 'https://install.syncromsp.com/agent.msi'"
Function Install-SyncroAgent ([string]$URL) {
# Exit if $URL is null or empty
If ([string]::IsNullOrEmpty($URL)) {
Write-Output "The URL parameter cannot be null or empty"
Exit 1
}
# Exit if running without admin rights
If (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Output "This script must be run as an administrator"
Exit 1
}
# Download and install the Syncro Agent
$Installer = "${Env:TEMP}\SyncroInstaller.msi"
Try { (New-Object System.Net.WebClient).DownloadFile($URL,$Installer) }
Catch { Write-Error "The Syncro Agent installer could not be downloaded from ${URL}`n"+$_.Exception.Message ; Exit 1 }
If ( Test-Path $Installer ) {
Start-Process msiexec.exe -ArgumentList "/i ""${Installer}"" /qn /norestart" -Wait
Remove-Item $Installer -Force -ErrorAction SilentlyContinue
} Else {
Write-Error "The Syncro Agent installer was successfully downloaded but could not be found at ""${Installer}"""
Exit 1
}
Exit 0
}
+1 -1
View File
@@ -10,4 +10,4 @@ If ( IsAdmin )
If ( $Installer ) { Install-Program -Path "${Installer}" -Arguments "/S" -Delete }
}
Else { LogMsg "Must be an administrator to install VLC" }
Else { Write-Output "Must be an administrator to install VLC" }
+5 -5
View File
@@ -10,19 +10,19 @@ If ( IsAdmin )
## Install package
$Installer = Download-File -URL $URL
Try { Start-Process "msiexec.exe" -ArgumentList "/i ${Installer} /qn /norestart DO_NOT_LAUNCH=1" -Wait -ErrorAction Stop }
Catch { LogErr $_.Exception.Message ; Exit }
Catch { Write-Error $_.Exception.Message ; Exit }
## Delete installer
Try { If ( Test-Path $Installer ) { Remove-Item $Installer -Force } }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Install manager service
Try { Start-Process "${Env:ProgramFiles}\WireGuard\wireguard.exe" -ArgumentList '/installmanagerservice' -Wait -ErrorAction SilentlyContinue }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Create shortcut on Public desktop
If ( $CreateDesktopShortcut ) {
Create-Shortcut -Path "${Env:Public}\Desktop\WireGuard VPN.LNK" -TargetPath "${Env:ProgramFiles}\WireGuard\wireguard.exe" -Description "WireGuard: Fast, Modern, Secure VPN Tunnel"
New-Shortcut -Path "${Env:Public}\Desktop\WireGuard VPN.LNK" -TargetPath "${Env:ProgramFiles}\WireGuard\wireguard.exe" -Description "WireGuard: Fast, Modern, Secure VPN Tunnel"
}
}
Else { LogMsg "Must be an administrator to install WireGuard" }
Else { Write-Output "Must be an administrator to install WireGuard" }
+11 -11
View File
@@ -20,7 +20,7 @@ If ( IsWindowsVersion -ge "6.1" )
## Check to make sure the ZeroTierOneService has started
## We'll only loop through this for a few seconds before failing
LogMsg "Attempting to start the ZeroTierOneService service..."
Write-Output "Attempting to start the ZeroTierOneService service..."
For ( $i = 1 ; $i -le 5 ; $i++ )
{
## Check to see if the service is running
@@ -28,7 +28,7 @@ If ( IsWindowsVersion -ge "6.1" )
{
## Attempt to stop/start the service
Try { Restart-Service -Name ZeroTierOneService -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Wait a few seconds before checking again
Start-Sleep -Seconds 5
@@ -37,7 +37,7 @@ If ( IsWindowsVersion -ge "6.1" )
## If the service is running, break the loop
Else
{
LogMsg " ZeroTierOneService service is started"
Write-Output " ZeroTierOneService service is started"
$ServiceRunning = $true ; break
}
}
@@ -54,27 +54,27 @@ If ( IsWindowsVersion -ge "6.1" )
If ( Test-Path "${ZTcli}" )
{
## Join the network, if $ZTcli exists
LogMsg "Joining ZeroTier network ${JoinNetwork}"
Write-Output "Joining ZeroTier network ${JoinNetwork}"
Try { Start-Process $ZTcli -ArgumentList "join ${JoinNetwork}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
Else { LogMsg "Could not find ""${ZTcli}""" }
Else { Write-Output "Could not find ""${ZTcli}""" }
}
## If the service still isn't running, just error out
Else { LogMsg " ZeroTierOneService could not be started in a timely fashion" }
Else { Write-Output " ZeroTierOneService could not be started in a timely fashion" }
}
Else { LogMsg "A ZeroTier network ID was not specified, will not join any network" }
Else { Write-Output "A ZeroTier network ID was not specified, will not join any network" }
## Remove the ZeroTier One Start Menu shortcut
$ShortcutPath = "${ProgramData}\Microsoft\Windows\Start Menu\Programs\ZeroTier One.lnk"
If ( Test-Path $ShortcutPath )
{
LogMsg "Deleting Start Menu shortcut from ""${ShortcutPath}"""
Write-Output "Deleting Start Menu shortcut from ""${ShortcutPath}"""
Try { Remove-Item $ShortcutPath -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
}
Else { LogMsg "ZeroTier can only be installed on Windows 7 or higher operating systems" }
Else { Write-Output "ZeroTier can only be installed on Windows 7 or higher operating systems" }
+60 -2
View File
@@ -1,9 +1,67 @@
# Management Scripts
Most scripts here require the Tools.ps1 script. You can dot source that script like this:
. $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1')))
```powershell
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
. $([Scriptblock]::Create((New-Object Net.WebClient).DownloadString('https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Tools.ps1')))
```
On older Windows PowerShell/.NET configurations, select TLS 1.2 **before** the first HTTPS download. The TLS setting inside `Tools.ps1` cannot help download `Tools.ps1` itself. See [Microsoft's PowerShell TLS guidance](https://learn.microsoft.com/en-us/security/engineering/solving-tls1-problem#update-windows-powershell-scripts-or-related-registry-settings).
You can then run any of the scripts using the Run-Script function:
Run-Script -LivePSScript Script-Name
If you're using an RMM tool, the scripts in the /rmm directory are all that's needed to hook the main script in this directory.
If you're using an RMM tool, the scripts in the /rmm directory are all that's needed to hook the main script in this directory.
For DSA collection, paste [rmm/Get-DSAManifest.ps1](rmm/Get-DSAManifest.ps1) into the RMM caller, preserving its configured platform variables. This caller selects TLS 1.2 before downloading, stops on bootstrap failure, and dot-sources the downloaded tools in the caller's scope. Updating the repository alone will not change an existing RMM caller.
For download failures, use the standalone [Test-FileDownload.ps1](Test-FileDownload.ps1) diagnostic. See [the RMM caller, report guide, and shared caller audit](docs/FileDownload-Diagnostics.md). It compares the existing helper, BITS, and direct GET without changing production download behavior or running downloaded files.
`Get-DSAManifest.ps1` uses `Download-FileDirectly`, which passes the original URL to BITS without preliminary `Get-AbsoluteURI` or `IsURLValid` requests. It accepts the same URL and optional `-File` arguments as `Download-File`, waits for completion, and throws on BITS errors. If `-File` is omitted, its temporary filename comes from the original URL; supply `-File` for extensionless URLs or URLs with query strings. Existing `Download-File` callers retain their previous behavior. Publish `Tools.ps1` together with `Get-DSAManifest.ps1` so the new helper is available.
## Converting RMM boolean variables
After dot-sourcing `Tools.ps1`, use `ConvertTo-RmmBoolean` with any RMM variable:
```powershell
$RestartEnabled = ConvertTo-RmmBoolean -Value $Restart -VariableName 'Restart'
If ( $RestartEnabled ) {
# Perform the requested restart.
}
# The variable name is optional; positional and pipeline input also work.
$ZipEnabled = ConvertTo-RmmBoolean $Zip
$UploadEnabled = $Upload | ConvertTo-RmmBoolean
```
Each input returns a `System.Boolean`: `true`, `$true`, `yes`, `y`, and `1` become `$true`; `false`, `$false`, `no`, `n`, `0`, blank strings, and `$null` become `$false`. Case and surrounding whitespace are ignored, and native boolean values also work. Other values throw an error; `-VariableName` identifies the input in that error. The function returns the converted value without changing the original variable; assign the result wherever needed.
`Get-DSAManifest.ps1` uses this helper for its three options. Publish it together with the updated `Tools.ps1` so the helper is available to the RMM caller.
## Uploading files
After dot-sourcing `Tools.ps1`, scripts can upload a completed file to the Emberkom FileDrop:
```powershell
Upload-File -Path 'C:\ProgramData\Emberkom\Output\report.zip'
```
`Get-DSAManifest.ps1` uploads its completed CSV or ZIP when `$Upload` is enabled. If `$DeleteAfterUpload` is also enabled, it deletes that same local file only after a confirmed successful upload. Disabled or failed uploads retain the file. `$Zip`, `$Upload`, and `$DeleteAfterUpload` all accept `true`, `$true`, `yes`, `y`, or `1`; `false`, `$false`, `no`, `n`, `0`, or an empty value disable the option. Casing and surrounding whitespace are ignored. The notification subject identifies the computer, and its message includes the source path and collection start time (local time with UTC offset).
The default destination is `https://xfer.emberkom.com/filedrop/cmd`. The sender defaults to the computer's hostname plus its primary DNS suffix, followed by `@emberkom.com` (for example, `pc01.example.com@emberkom.com`). Without a DNS suffix, it uses the hostname. Override these with `-FileDropUrl` and `-From`; `-Subject` and `-Message` are also optional.
`Upload-File` uses the [LiquidFiles FileDrop API](https://docs.liquidfiles.com/api/v4.3/filedrop/) to obtain a temporary token, upload the file, and submit the message that triggers FileDrop delivery and its configured email notification. It returns a receipt with the path, destination, sender, attachment ID, byte count, and server status. It retains the local file and throws on failure.
Uploads use [binary chunks](https://docs.liquidfiles.com/api/v4.3/attachments/chunks.html) with a default 10 MiB buffer, so large files do not need to fit in memory. The FileDrop's size and extension restrictions are checked before upload. `-ChunkSizeMB`, `-RetryCount`, and `-TimeoutSeconds` control chunk size, retries, and each request's timeout; `-Verbose` displays transfer details.
Allow the RMM job to run for the entire upload. The function waits for completion and retries failed chunks within that run; it does not resume across process restarts. LiquidFiles tokens expire after 24 hours. Final submission is attempted once: if its response is lost, check the FileDrop before rerunning to avoid duplicate notifications.
Offline tests (no uploads or notifications):
```powershell
powershell.exe -NoProfile -File .\tests\Upload-File.Tests.ps1
powershell.exe -NoProfile -File .\tests\Get-DSAManifest.Tests.ps1
powershell.exe -NoProfile -File .\tests\Download-File.Tests.ps1
powershell.exe -NoProfile -File .\tests\ConvertTo-RmmBoolean.Tests.ps1
```
+47
View File
@@ -0,0 +1,47 @@
# Make sure we're booted into Safe Mode with Networking
If ( !((Get-CimInstance -ClassName Win32_ComputerSystem).BootupState.ToLower() -eq "fail-safe with network boot") ) {
Write-Output "Cannot run script because the system is not currently booted into Safe Mode with Networking"
Return
}
# Download the ESET Uninstaller
Write-Output "Downloading ESET Uninstaller to: ${ESETUninstaller}"
$ESETUninstallerURL = "https://download.eset.com/com/eset/tools/installers/eset_apps_remover/latest/esetuninstaller.exe"
Download-File -URL $ESETUninstallerURL -File $ESETUninstaller
If ( !(Test-Path $ESETUninstaller) ) { Write-Error "ERROR: ESET Uninstaller could not be downloaded, this script will exit" ; Return }
# Backup network interfaces and settings
$InterfacesFile = "${Global:OutputDirectory}\interfaces.dat"
If ( Test-Path $InterfacesFile ) { Write-Output "Removing existing interface export: ${InterfacesFile}" ; Remove-Item -Path $InterfacesFile -Force }
Write-Output "Exporting interfaces to: ${InterfacesFile}"
Try { & netsh.exe -c interface dump | Out-File $InterfacesFile }
Catch { Write-Error "ERROR: Interfaces could not be exported, this script will exit" ; Return }
# Uninstall all ESET products
Write-Output "Uninstalling all ESET products"
& "cmd.exe /k ""${ESETUninstaller}"" /mode=online /force"
# Restore network interfaces and settings
If ( Test-Path $InterfacesFile ) {
Write-Output "Restoring interfaces from export: ${InterfacesFile}"
Try { & netsh.exe exec $InterfacesFile }
Catch { Write-Error "ERROR: Interfaces could not be restored!" }
}
# Ensure safe mode is disabled
Write-Output "Disabling safe mode"
$command = "${Env:SystemRoot}\System32\bcdedit.exe"
$params = "/deletevalue {default} safeboot".Split(" ")
Try { & "${command}" $params }
Catch {
If ( $_.Exception.Message -match "Element not found" ) { Write-Output " - Safe mode has already been disabled" }
Else { Write-Error "ERROR: could not disable safe mode, manual intervention may be required" }
}
# Reboot into normal mode
Write-Output "Rebooting into normal mode"
& "${Env:SystemRoot}\System32\shutdown.exe" -r -f -t 60 -c "Operation complete. The computer will restart in 60 seconds."
+3 -3
View File
@@ -5,8 +5,8 @@ End-Process -Name "DeltekVision" -Force
$APP_PATH = "${Env:UserProfile}\AppData\Local\Apps\2.0"
If ( Test-Path $APP_PATH ) {
## Delete the existing installed app
LogMsg "Deleting ""${APP_PATH}"""
Write-Output "Deleting ""${APP_PATH}"""
Try { Remove-Item $APP_PATH -Recurse -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
} Else { LogMsg "The path specified does not exist: ${APP_PATH}" }
} Else { Write-Output "The path specified does not exist: ${APP_PATH}" }
+39
View File
@@ -0,0 +1,39 @@
# Define the username of the provisioning admin account
$ProvisioningAdminUsername = "ek-provadmin"
# Exit if this is a domain controller
If ( $(Get-CimInstance -ClassName Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType) -eq "2" ) {
Write-Output "Cannot create or modify local accounts on a domain controller"
Exit 0
}
# Get the provisioning admin user account
$ProvisioningAdmin = Get-LocalUser -Name $ProvisioningAdminUsername -ErrorAction SilentlyContinue
# Exit if the provisioning admin account does not exist
If ( $ProvisioningAdmin -eq $false ) { Write-Output "Provisioning Admin account does not exist" ; Exit 0 }
# Exit if the user is logged in
If ( $(Get-CimInstance -Class Win32_Process -Filter 'name = "explorer.exe"' | Invoke-CimMethod -MethodName getowner).User -contains $ProvisioningAdminUsername ) {
Write-Output "User is logged in, cannot complete removal of provisioning admin account"
Exit 1
}
# Remove the provisioning admin account
$SID = $ProvisioningAdmin.SID.Value
Write-Output "Removing provisioning admin account`nUsername: ${ProvisioningAdminUsername}`nSID: $SID"
Try { Remove-LocalUser -SID $SID -ErrorAction Stop }
Catch { Write-Error "The provisioning admin account could not be removed`n"+$_.Exception.Message ; Exit 1 }
# Fully remove the provisioning admin profile
Write-Output "Removing provisioning admin CIM instance"
Try { Get-CimInstance -Class Win32_UserProfile | Where-Object { $_.SID -eq $SID } -ErrorAction SilentlyContinue | Remove-CimInstance }
Catch { Write-Error "The provisioning admin profile could not be removed`n"+$_.Exception.Message ; Exit 1 }
# Delete any remaining provisioning admin directories
$ProfilePath = "${Env:SystemDrive}\Users\$ProvisioningAdminUsername"
If ( Test-Path $ProfilePath ) {
Write-Output "Deleting provisioning admin profile directory: ${ProfilePath}"
Try { Remove-Item $ProfilePath -Recurse -Force }
Catch { Write-Error "The provisioning admin profile directory could not be removed`n"+$_.Exception.Message ; Exit 1 }
}
+6 -6
View File
@@ -3,12 +3,12 @@
If ( $OlderThanDays -is [int] ) {
If ( Test-Path $Path ) {
LogMsg "Deleting all items older than ${OlderThanDays} days in ""${Path}"""
$files = Get-ChildItem $Path | Where { !$_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-${OlderThanDays}) }
Write-Output "Deleting all items older than ${OlderThanDays} days in ""${Path}"""
$files = Get-ChildItem $Path | Where-Object { !$_.PSIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-${OlderThanDays}) }
Foreach ( $f in $files ) {
$msg = "Deleting {0}" -f $f.FullName ; LogMsg $msg
$msg = "Deleting {0}" -f $f.FullName ; Write-Output $msg
Try { Remove-Item $f -Force | Out-Null }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
} Else { LogMsg "Path does not exist: ""${Path}""" }
} Else { LogMsg "The value entered for ""OlderThanDays"" is not an integer" }
} Else { Write-Output "Path does not exist: ""${Path}""" }
} Else { Write-Output "The value entered for ""OlderThanDays"" is not an integer" }
+1 -1
View File
@@ -21,7 +21,7 @@ ForEach ( $path in $RevitLocalPaths ) {
# Only remove Revit local files that have worksharing enabled
If ( (Test-Path "${revitfile}") -and (Test-Path "${BackupDir}")) {
LogMsg "Deleting ""${revitfile}"" and backup directory ""${BackupDir}"""
Write-Output "Deleting ""${revitfile}"" and backup directory ""${BackupDir}"""
Remove-Item "${revitfile}" -Force -ErrorAction SilentlyContinue
Remove-Item "${BackupDir}" -Recurse -ErrorAction SilentlyContinue
}
+21
View File
@@ -0,0 +1,21 @@
# Function to restart an endpoint that has not been restarted for the specified number of hours
Function Restart-EndpointOnUptime ([int]$MaxUptimeHours) {
# Exit if the specified number of hours is not greater than 0
If ( !($MaxUptimeHours -gt 0) ) { Write-Output "Must specify 1 or more hours" ; Exit }
# Get the last boot up time as a DateTime
$LastBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($(Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object -ExpandProperty LastBootUpTime))
# Get the total number of hours the endpoint has been up
$UptimeHours = $($(Get-Date) - $LastBootTime).TotalHours
# If $Uptime is greater than $MaxUptimeHours, restart the endpoint
If ( $UptimeHours -gt $MaxUptimeHours) {
Write-Output "This endpoint has been up for at least ${UptimeHours} hours and will be restarted now"
Start-Sleep -Seconds 5
Restart-Computer -Force
} Else {
Write-Output "This endpoint has already been restarted within the last ${MaxUptimeHours} hours"
}
}
+7
View File
@@ -0,0 +1,7 @@
If ( $(Get-CimInstance -ClassName Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType) -eq "2" ) {
Write-Output "Cannot create or modify local accounts on a domain controller"
Return
}
Write-Output "Setting local Administrator password"
Try { net user Administrator $Password /times:all /active:yes }
Catch { Write-Error "Could not set local Administrator password`n"+$_.Exception.Message ; Exit 1 }
-7
View File
@@ -1,7 +0,0 @@
$LogsToEnable = @(
''
)
ForEach ( $log in $LogsToEnable ) {
Toggle-Log -Name $log -Enable
}
+91
View File
@@ -0,0 +1,91 @@
# Exit if not running as administrator
If ( -not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") ) { Write-Output "This script must be run as an administrator" ; Exit 1 }
# Get the operating system CIM instance
Try { $OS = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop }
Catch { Write-Error "Could not get Win32_OperatingSystem CIM instance`n"+$_.Exception.Message ; Exit 1 }
# Exit if the endpoint is not running Windows 10
If ( ($OS.Caption -notmatch "Windows 10") ) { Write-Output "This endpoint is not running Windows 10" ; Exit 1 }
Else { Write-Output "This endpoint is running Windows 10" }
# Exit if the endpoint is not running a Pro/Enterprise edition of Windows
If ( ($OS.Caption -notmatch "Pro") -and ($OS.Caption -notmatch "Enterprise") ) { Write-Output "Only Pro/Enterprise versions of Windows can be upgraded" ; Exit 1 }
Else { Write-Output "This endpoint is running a Pro/Enterprise version of Windows" }
# Exit if the endpoint is a server edition
Try
{
If ( $OS.ProductType -gt 1 ) { Write-Output "Server operating system will not be upgraded" ; Exit 1 }
Else { Write-Output "This endpoint is not a server operating system" }
}
Catch { Write-Error "The operating system type could not be determined`n"+$_.Exception.Message ; Exit 1 }
# Exit if the endpoint does not have at least 4GB of RAM
Try
{
If ( (Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction Stop | Measure-Object -Property Capacity -Sum).Sum -lt 4GB ) { Write-Output "This endpoint does not have at least 4GB of RAM" ; Exit 1 }
Else { Write-Output "This endpoint has at least 4GB of RAM" }
}
Catch { Write-Error "The amount of RAM could not be determined`n"+$_.Exception.Message ; Exit 1 }
# Exit if the endpoint does not have at least 64GB of storage on the system drive
Try
{
$SystemDrive = $Env:SystemDrive.TrimEnd('\')
If ( (Get-CimInstance -ClassName Win32_Volume -Filter "DriveLetter = '$SystemDrive'" -ErrorAction Stop).FreeSpace -lt 64GB ) { Write-Output "This endpoint does not have at least 64GB of storage on the system drive" ; Exit 1 }
Else { Write-Output "This endpoint has at least 64GB of free space on the system drive" }
}
Catch { Write-Error "The free space on the system drive could not be determined`n"+$_.Exception.Message ; Exit 1 }
# Exit if TPM does not exist or is not version 2.0
Try
{
$TPM2Present = $false
ForEach ( $specVersion in (Get-CimInstance -Namespace "root\CIMV2\Security\MicrosoftTpm" -Class Win32_Tpm -ErrorAction Stop).SpecVersion )
{
If ( $specVersion -ge 2.0 ) { $TPM2Present = $true ; Break }
}
If ( $TPM2Present -eq $false ) { Write-Output "TPM does not support version 2.0 specification" ; Exit 1 }
Else { Write-Output "This endpoint has a TPM compliant with the version 2.0 specification" }
}
Catch { Write-Error "TPM is not present or its version cannot be determined`n"+$_.Exception.Message ; Exit 1 }
# Exit if Secure Boot is not enabled
Try
{
If ( (Confirm-SecureBootUEFI -ErrorAction Stop) -eq $false ) { Write-Output "Secure Boot is not enabled" ; Exit 1 }
Else { Write-Output "This endpoint has Secure Boot enabled" }
}
Catch { Write-Output "Secure Boot state cannot be determined.`n"+$_.Exception.Message ; Exit 1 }
# Uninstall previous Windows 11 Update Assistant
$WIAPath = "${Env:ProgramFiles(x86)}\WindowsInstallationAssistant\Windows10UpgraderApp.exe"
If ( Test-Path $WIAPath )
{
Write-Output "Uninstalling existing Windows Installation Assistant"
Get-Process -Name "Windows10UpgraderApp" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Try { Start-Process $WIAPath -ArgumentList '/ForceUninstall' -Wait }
Catch { Write-Error $_.Exception.Message ; Exit 1}
} Else { Write-Output "This endpoint does not have the Windows Installation Assistant installed" }
# URL to the Windows Installation Assistant
$URL = 'https://go.microsoft.com/fwlink/?linkid=2171764'
# Download Windows Installation Assistant
$WIAInstaller = "${Env:TEMP}\Windows11InstallationAssistant.exe"
If ( Test-Path $WIAInstaller )
{
Write-Output "Removing existing Windows Installation Assistant installer: ${WIAInstaller}"
Remove-Item $WIAInstaller -Force -ErrorAction SilentlyContinue
}
Write-Output "Downloading ${URL} to ""${WIAInstaller}"""
Try { (New-Object System.Net.WebClient).DownloadFile($URL,$WIAInstaller) }
Catch { Write-Error "The Windows Installation Assistant could not be downloaded`n"+$_.Exception.Message ; Exit 1 }
# Install and run the Windows Installation Assistant
If ( -not (Test-Path $WIAInstaller) ) { Write-Error "The Windows Installation Assistant could not be found after successfully downloading" ; Exit 1 }
Write-Output "Launching upgrade"
Try { Start-Process -FilePath $WIAInstaller -ArgumentList "/QuietInstall /SkipEULA /Auto Upgrade /SkipSelfUpdate /ShowProgressInTaskBarIcon /SkipCompatCheck" }
Catch { Write-Output $_.Exception.Message ; Exit 1 }
Exit 0
+11 -11
View File
@@ -13,34 +13,34 @@ If ( IsWindowsVersion -ge "10.0" ) {
## Uninstall previous Windows 10 Update Assistant
If ( Test-Path "${Env:SystemDrive}\Windows10Upgrade\Windows10UpgraderApp.exe" )
{
LogMsg "Uninstalling existing Windows 10 Update Assistant"
Write-Output "Uninstalling existing Windows 10 Update Assistant"
Try { Start-Process "${Env:SystemDrive}\Windows10Upgrade\Windows10UpgraderApp.exe" -ArgumentList '/ForceUninstall' -Wait }
Catch { LogMsg $_.Exception.Message }
Catch { Write-Output $_.Exception.Message }
}
## Download Windows 10 Update Assistant
$FILENAME = Split-Path $URL -leaf
If ( Test-Path "${DOWNLOADS}\${FILENAME}" )
{
LogMsg "Deleting existing installer: ${DOWNLOADS}\${FILENAME}"
Write-Output "Deleting existing installer: ${DOWNLOADS}\${FILENAME}"
Try { Remove-Item "${DOWNLOADS}\${FILENAME}" -Force -ErrorAction SilentlyContinue }
Catch { LogMsg $_.Exception.Message }
Catch { Write-Output $_.Exception.Message }
}
LogMsg "Downloading ${URL} to ${DOWNLOADS}\${FILENAME}"
Write-Output "Downloading ${URL} to ${DOWNLOADS}\${FILENAME}"
Try { (New-Object System.Net.WebClient).DownloadFile($URL,"${DOWNLOADS}\${FILENAME}") }
Catch { LogMsg $_.Exception.Message }
Catch { Write-Output $_.Exception.Message }
## Install and run the Windows 10 Update Assistant
If ( Test-Path "${DOWNLOADS}\${FILENAME}" )
{
LogMsg "Launching installer: ${DOWNLOADS}\${FILENAME}"
Write-Output "Launching installer: ${DOWNLOADS}\${FILENAME}"
Try { Start-Process -FilePath "${DOWNLOADS}\${FILENAME}" }
Catch { LogMsg $_.Exception.Message }
Catch { Write-Output $_.Exception.Message }
}
Else { LogMsg "${DOWNLOADS}\${FILENAME} does not exist" }
Else { Write-Output "${DOWNLOADS}\${FILENAME} does not exist" }
}
Else { LogMsg "This endpoint is already up to date." }
Else { Write-Output "This endpoint is already up to date." }
}
Else { LogMsg "This endpoint is not running Windows 10." }
Else { Write-Output "This endpoint is not running Windows 10." }
+540
View File
@@ -0,0 +1,540 @@
#Requires -Version 2.0
<#
.SYNOPSIS
Compares download paths without changing the production downloader or TLS settings.
.DESCRIPTION
Run from the failing RMM caller, after its normal Tools.ps1 bootstrap, using the
same account and PowerShell executable. A standalone run skips the helper probe
when Download-File is not loaded. Never dot-sources a downloaded payload.
Each probe runs in a bounded child process. Only that run's BITS jobs and scratch
files are removed. Reports remain in a new subdirectory of OutputDirectory.
The source deliberately uses PowerShell 2 syntax and .NET 2-era APIs. Actual
PowerShell 2, 3, and 4 runtime validation is still required on legacy machines.
.PARAMETER URL
HTTP or HTTPS file URL. Downloads are never executed.
.PARAMETER OutputDirectory
Report parent directory; defaults to the account's temporary directory.
.PARAMETER TimeoutSeconds
Wall-clock limit for each probe, including download and hashing. Defaults to 120.
.EXAMPLE
& .\Test-FileDownload.ps1 -URL 'https://example.com/tool.exe'
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)][string]$URL,
[string]$OutputDirectory = [IO.Path]::GetTempPath(),
[ValidateRange(1,3600)][int]$TimeoutSeconds = 120
)
# All diagnostic state is local even when the caller dot-sources this script.
& {
param($FdUrlText, $FdOutputParent, $FdTimeout)
$ErrorActionPreference = 'Stop'
$FdUri = New-Object Uri $FdUrlText
if (!$FdUri.IsAbsoluteUri -or @('http','https') -notcontains $FdUri.Scheme -or $FdUri.UserInfo) {
throw 'URL must be an absolute HTTP(S) URL without embedded credentials.'
}
if ($PSVersionTable.PSVersion.Major -gt 5) { throw 'Run this diagnostic in Windows PowerShell 2-5.1, using the same shell as the RMM.' }
$FdRunId = [guid]::NewGuid().ToString('N')
$FdParent = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FdOutputParent)
$FdRoot = Join-Path $FdParent ('file-download-diagnostic-' + $FdRunId)
$null = New-Item -Path $FdRoot -ItemType Directory -ErrorAction Stop
$FdWork = Join-Path $FdRoot 'work'
$null = New-Item -Path $FdWork -ItemType Directory -ErrorAction Stop
$FdExe = [Diagnostics.Process]::GetCurrentProcess().MainModule.FileName
if ([IO.Path]::GetFileName($FdExe) -ine 'powershell.exe') {
throw 'Run from powershell.exe rather than ISE or an embedded host so child probes can use the same executable safely.'
}
$FdExtension = [IO.Path]::GetExtension($FdUri.AbsolutePath)
if ($FdExtension -notmatch '^\.[A-Za-z0-9]{1,16}$') { $FdExtension = '.download' }
function Get-FdTextHash($Text) {
$FdHash = [Security.Cryptography.SHA256]::Create()
try { return [BitConverter]::ToString($FdHash.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text))).Replace('-','') }
finally { $FdHash.Clear() }
}
function Export-FdXml($Value,$Path,$Depth) {
# PowerShell 2 lacks some LiteralPath parameter sets. A scoped drive keeps
# wildcard characters in the parent directory out of Export-Clixml's path.
$Drive = 'FdXml' + [guid]::NewGuid().ToString('N')
$null = New-PSDrive -Name $Drive -PSProvider FileSystem -Root ([IO.Path]::GetDirectoryName($Path)) -Scope Local
try { $Value | Export-Clixml -Path ($Drive + ':\' + [IO.Path]::GetFileName($Path)) -Depth $Depth }
finally { Remove-PSDrive -Name $Drive -Scope Local }
}
$FdCommands = @()
foreach ($FdName in @('Download-File','Get-AbsoluteURI','IsURLValid','Get-TempPath','Write-Error','Write-Output','Start-BitsTransfer')) {
$FdCommand = Get-Command -Name $FdName -ErrorAction SilentlyContinue | Select-Object -First 1
$FdDefinition = $null
if ($FdCommand -and @('Function','Filter') -contains [string]$FdCommand.CommandType) { $FdDefinition = $FdCommand.Definition }
$FdCommands += New-Object PSObject -Property @{
Name=$FdName; CommandType=[string]$FdCommand.CommandType; ModuleName=$FdCommand.ModuleName
ModuleVersion=[string]$FdCommand.Module.Version; Definition=$FdDefinition
DefinitionSHA256=$(if ($null -ne $FdDefinition) { Get-FdTextHash $FdDefinition } else { $null })
ResolvedDefinition=$FdCommand.Definition
}
}
$FdContext = New-Object PSObject -Property @{
RunId=$FdRunId; URL=$FdUri.AbsoluteUri; Commands=$FdCommands
SecurityProtocol=[int][Net.ServicePointManager]::SecurityProtocol
CertificateCallbackPresent=($null -ne [Net.ServicePointManager]::ServerCertificateValidationCallback)
ProxyType=$(if ([Net.WebRequest]::DefaultWebProxy) { [Net.WebRequest]::DefaultWebProxy.GetType().FullName } else { 'None' })
ProxyAddress=[string]([Net.WebRequest]::DefaultWebProxy.Address)
PowerShellVersion=[string]$PSVersionTable.PSVersion; CLRVersion=[string][Environment]::Version
ProcessBits=([IntPtr]::Size * 8); Executable=$FdExe
Identity=[Security.Principal.WindowsIdentity]::GetCurrent().Name
WorkingDirectory=(Get-Location).Path; TimeoutSeconds=$FdTimeout; DestinationExtension=$FdExtension
}
$FdContextPath = Join-Path $FdWork 'context.clixml'
Export-FdXml $FdContext $FdContextPath 12
# This is diagnostic code, not code fetched from the URL. It is copied into each
# fresh child so leaked connections and preference changes cannot cross probes.
$FdWorker = {
param($InputPath)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$InputData = Import-Clixml -Path ([Management.Automation.WildcardPattern]::Escape($InputPath))
$Context = Import-Clixml -Path ([Management.Automation.WildcardPattern]::Escape($InputData.ContextPath))
$Result = New-Object PSObject -Property @{
Name=$InputData.Name; Method=$InputData.Method; ExistingEmpty=$InputData.ExistingEmpty
Status='Running'; StartedUtc=[DateTime]::UtcNow.ToString('o'); ElapsedSeconds=0
Errors=@(); Notes=@(); Http=@(); Bits=@(); Snapshots=@(); HelperOutput=@(); HelperBitsSource=@(); Environment=$null
CleanupErrors=@(); ChildPowerShell=[string]$PSVersionTable.PSVersion
ChildCLR=[string][Environment]::Version; ChildIdentity=[Security.Principal.WindowsIdentity]::GetCurrent().Name
ChildProcessBits=([IntPtr]::Size * 8)
}
$Watch = [Diagnostics.Stopwatch]::StartNew()
function Export-FdXml($Value,$Path,$Depth) {
$Drive = 'FdXml' + [guid]::NewGuid().ToString('N')
$null = New-PSDrive -Name $Drive -PSProvider FileSystem -Root ([IO.Path]::GetDirectoryName($Path)) -Scope Local
try { $Value | Export-Clixml -Path ($Drive + ':\' + [IO.Path]::GetFileName($Path)) -Depth $Depth }
finally { Remove-PSDrive -Name $Drive -Scope Local }
}
function Save-Checkpoint {
$Result.ElapsedSeconds = [Math]::Round($Watch.Elapsed.TotalSeconds,3)
# Replace only this probe's checkpoint; the parent reads it after child exit.
Export-FdXml $Result $InputData.ResultPath 16
}
function Get-ErrorDetail($Record) {
return New-Object PSObject -Property @{
Message=$Record.Exception.Message; Exception=$Record.Exception.ToString()
FullyQualifiedErrorId=$Record.FullyQualifiedErrorId
Category=[string]$Record.CategoryInfo; Position=$Record.InvocationInfo.PositionMessage
Record=($Record | Format-List * -Force | Out-String -Width 240)
}
}
function Get-FileSnapshot($Label) {
$Snapshot = New-Object PSObject -Property @{Label=$Label; TimeUtc=[DateTime]::UtcNow.ToString('o'); Exists=$false; Length=$null; LengthAfterHash=$null; SHA256=$null; FirstBytes=$null; Error=$null}
$Stream = $null; $Hash = $null
try {
if (Test-Path -LiteralPath $InputData.Destination -PathType Leaf) {
$Snapshot.Exists = $true
$Snapshot.Length = (Get-Item -LiteralPath $InputData.Destination).Length
$Stream = [IO.File]::Open($InputData.Destination,[IO.FileMode]::Open,[IO.FileAccess]::Read,([IO.FileShare]::ReadWrite -bor [IO.FileShare]::Delete))
$Prefix = New-Object byte[] 8
$Count = $Stream.Read($Prefix,0,$Prefix.Length)
if ($Count -gt 0) { $Snapshot.FirstBytes = [BitConverter]::ToString($Prefix,0,$Count) }
$Stream.Position = 0
$Hash = [Security.Cryptography.SHA256]::Create()
$Snapshot.SHA256 = [BitConverter]::ToString($Hash.ComputeHash($Stream)).Replace('-','')
$Snapshot.LengthAfterHash = (Get-Item -LiteralPath $InputData.Destination).Length
}
} catch { $Snapshot.Error = Get-ErrorDetail $_ }
finally { if ($Stream) { $Stream.Dispose() }; if ($Hash) { $Hash.Clear() } }
return $Snapshot
}
function Add-BitsSnapshot($Job, $Stage) {
$Result.Bits += New-Object PSObject -Property @{
Stage=$Stage; TimeUtc=[DateTime]::UtcNow.ToString('o'); JobId=[string]$Job.JobId
JobState=[string]$Job.JobState; BytesTotal=$Job.BytesTotal; BytesTransferred=$Job.BytesTransferred
FilesTotal=$Job.FilesTotal; FilesTransferred=$Job.FilesTransferred
ErrorCode=$Job.ErrorCode; ErrorContext=[string]$Job.ErrorContext; ErrorDescription=$Job.ErrorDescription
ProxyUsage=[string]$Job.ProxyUsage; OwnerAccount=$Job.OwnerAccount
}
Save-Checkpoint
}
function Remove-OwnedBitsJobs {
try {
Import-Module BitsTransfer -ErrorAction Stop
$Jobs = @(BitsTransfer\Get-BitsTransfer -ErrorAction Stop | Where-Object {
$_.DisplayName -eq $InputData.Tag -and $_.Description -eq $Context.RunId
})
foreach ($Job in $Jobs) {
Add-BitsSnapshot $Job 'Before cleanup'
BitsTransfer\Remove-BitsTransfer -BitsJob $Job -ErrorAction Stop
}
} catch { $Result.CleanupErrors += Get-ErrorDetail $_ }
}
function Add-HttpSnapshot($Response, $Method, $RequestUri) {
$Headers = @{}
foreach ($Header in @('Content-Length','Content-Range','Content-Type','Transfer-Encoding','Content-Encoding','Accept-Ranges','ETag','Last-Modified','Location','Via','Age','Server')) {
$Headers[$Header] = $Response.Headers[$Header]
}
$Result.Http += New-Object PSObject -Property @{
Method=$Method; RequestUri=$RequestUri; ResponseUri=$Response.ResponseUri.AbsoluteUri
Status=[int]$Response.StatusCode; ContentLength=$Response.ContentLength; Headers=$Headers
}
Save-Checkpoint
}
function Open-HttpResponse($Method, $UseRange) {
$CurrentUri = New-Object Uri $Context.URL
for ($Redirect = 0; $Redirect -le 10; $Redirect++) {
$Request = [Net.HttpWebRequest]::Create($CurrentUri)
$Request.Method = $Method
$Request.AllowAutoRedirect = $false
$Request.Timeout = $Context.TimeoutSeconds * 1000
$Request.ReadWriteTimeout = $Context.TimeoutSeconds * 1000
if ($UseRange) { $Request.AddRange(0,15) }
try { $Response = $Request.GetResponse() }
catch {
if ($_.Exception.Response) {
try { Add-HttpSnapshot $_.Exception.Response $Method $CurrentUri.AbsoluteUri }
finally { $_.Exception.Response.Close() }
}
throw
}
Add-HttpSnapshot $Response $Method $CurrentUri.AbsoluteUri
if (@(301,302,303,307,308) -contains [int]$Response.StatusCode) {
try { $NextUri = New-Object Uri $CurrentUri, $Response.Headers['Location'] }
finally { $Response.Close() }
if (@('http','https') -notcontains $NextUri.Scheme -or $NextUri.UserInfo) { throw 'Unsupported redirect target.' }
if ($CurrentUri.Scheme -eq 'https' -and $NextUri.Scheme -eq 'http') { throw 'HTTPS-to-HTTP redirect recorded but not followed.' }
$CurrentUri = $NextUri
} else { return $Response }
}
throw 'HTTP redirect limit exceeded.'
}
try {
Save-Checkpoint
# A child does not inherit this process-local .NET setting. Reproduce the
# caller's exact value, rather than selecting a new protocol or changing Windows.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]$Context.SecurityProtocol
if ($InputData.Method -eq 'Cleanup') {
Remove-OwnedBitsJobs
$Result.Status = 'Complete'
} elseif ($InputData.Method -eq 'Environment') {
$Info = @{}
$Result.Environment = $Info
try { $Info['OS'] = Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object Caption,Version,BuildNumber,OSArchitecture,ServicePackMajorVersion }
catch { $Info['OSError'] = Get-ErrorDetail $_ }
Save-Checkpoint
$Info['BitsService'] = Get-Service -Name BITS -ErrorAction SilentlyContinue | Select-Object Name,Status,StartType
$Info['BitsModules'] = @(Get-Module -ListAvailable -Name BitsTransfer | Select-Object Name,Version,Path)
$BitsBinary = Join-Path $Env:WINDIR 'System32\qmgr.dll'
$NativeBitsBinary = Join-Path $Env:WINDIR 'Sysnative\qmgr.dll'
if (Test-Path -LiteralPath $NativeBitsBinary) { $BitsBinary = $NativeBitsBinary }
$Info['BitsBinaryPath'] = $BitsBinary
try { $Info['BitsBinaryVersion'] = [Diagnostics.FileVersionInfo]::GetVersionInfo($BitsBinary).FileVersion }
catch { $Info['BitsBinaryVersionError'] = Get-ErrorDetail $_ }
$Info['DotNetProxyType'] = $Context.ProxyType
$Info['CallerProxyAddress'] = $Context.ProxyAddress
$Info['DotNetSecurityProtocol'] = [string][Net.ServicePointManager]::SecurityProtocol
$Info['CertificateCallbackPresent'] = $Context.CertificateCallbackPresent
Save-Checkpoint
$Info['WinHttpProxy'] = (& "$Env:WINDIR\System32\netsh.exe" winhttp show proxy 2>&1 | Out-String)
$Info['WinHttpRegistryViews'] = @{}
foreach ($View in @('32','64')) {
# reg.exe supports explicit views without requiring the .NET 4 RegistryView API.
try { $Info['WinHttpRegistryViews'][$View] = (& "$Env:WINDIR\System32\reg.exe" query 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp' /v DefaultSecureProtocols ("/reg:" + $View) 2>&1 | Out-String) }
catch { $Info['WinHttpRegistryViews'][$View] = $_.Exception.Message }
}
$Info['Registry'] = @()
foreach ($Key in @(
'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings',
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp',
'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp',
'HKLM:\SOFTWARE\Microsoft\.NETFramework\v2.0.50727',
'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319',
'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v2.0.50727',
'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319',
'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client',
'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client',
'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client'
)) {
$Values = Get-ItemProperty -LiteralPath $Key -ErrorAction SilentlyContinue
$Info['Registry'] += New-Object PSObject -Property @{
Key=$Key; Present=($null -ne $Values); Enabled=$Values.Enabled; DisabledByDefault=$Values.DisabledByDefault
DefaultSecureProtocols=$Values.DefaultSecureProtocols; SchUseStrongCrypto=$Values.SchUseStrongCrypto
SystemDefaultTlsVersions=$Values.SystemDefaultTlsVersions
ProxyEnable=$Values.ProxyEnable; ProxyServer=$Values.ProxyServer; AutoConfigURL=$Values.AutoConfigURL
}
}
Save-Checkpoint
$Info['DotNetResolvedProxy'] = $(if ([Net.WebRequest]::DefaultWebProxy) { [Net.WebRequest]::DefaultWebProxy.GetProxy((New-Object Uri $Context.URL)).AbsoluteUri } else { 'None' })
$Result.Status = 'Complete'
} elseif ($InputData.Method -eq 'Headers') {
foreach ($HttpMethod in @('HEAD','Range')) {
$Response = $null
try {
if ($HttpMethod -eq 'HEAD') { $Response = Open-HttpResponse 'HEAD' $false }
else { $Response = Open-HttpResponse 'GET' $true }
} catch { $Result.Errors += Get-ErrorDetail $_ }
finally { if ($Response) { $Response.Close() } }
}
$Result.Status = $(if ($Result.Errors.Count) { 'Failed' } else { 'Complete' })
} else {
if ($InputData.ExistingEmpty) { [IO.File]::WriteAllBytes($InputData.Destination,(New-Object byte[] 0)) }
$Result.Snapshots += Get-FileSnapshot 'Before transfer'
Save-Checkpoint
if ($InputData.Method -eq 'Helper') {
$Helper = @($Context.Commands | Where-Object { $_.Name -eq 'Download-File' -and $_.Definition })
$BitsResolution = $Context.Commands | Where-Object { $_.Name -eq 'Start-BitsTransfer' }
if ($Helper.Count -eq 0) {
$Result.Status = 'Skipped'
$Result.Notes += 'Download-File was not a loaded function; no Tools.ps1 was fetched or sourced.'
} elseif ($BitsResolution.CommandType -ne 'Cmdlet' -or $BitsResolution.ModuleName -ne 'BitsTransfer') {
$Result.Status = 'Skipped'
$Result.Notes += 'Start-BitsTransfer is shadowed or unavailable. Definition recorded; helper not run because its jobs cannot be safely tagged.'
} else {
Import-Module BitsTransfer -ErrorAction Stop
foreach ($Command in $Context.Commands) {
if ($Command.Definition -and $Command.Name -ne 'Start-BitsTransfer') {
. ([scriptblock]::Create(('function global:{0} {{ {1} }}' -f $Command.Name,$Command.Definition)))
}
}
$Global:LogFile = $InputData.LogPath
# Preserve all native BITS parameters; add only ownership tags so
# a timed-out synchronous job can be identified without touching others.
$Global:FdNativeBits = Get-Command BitsTransfer\Start-BitsTransfer
$Global:FdBitsTag = $InputData.Tag
$Global:FdBitsRun = $Context.RunId
$Global:FdWorkerResult = $Result
function global:Start-BitsTransfer {
[CmdletBinding()] param()
dynamicparam {
$Dictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary
foreach ($Parameter in $Global:FdNativeBits.Parameters.Values) {
if (@('Verbose','Debug','ErrorAction','WarningAction','InformationAction','ErrorVariable','WarningVariable','InformationVariable','OutVariable','OutBuffer','PipelineVariable','ProgressAction') -notcontains $Parameter.Name) {
$Attributes = New-Object 'Collections.ObjectModel.Collection[Attribute]'
foreach ($Attribute in $Parameter.Attributes) { $Attributes.Add($Attribute) }
$Dynamic = New-Object Management.Automation.RuntimeDefinedParameter $Parameter.Name,$Parameter.ParameterType,$Attributes
$Dictionary.Add($Parameter.Name,$Dynamic)
}
}
return $Dictionary
}
process {
$Global:FdWorkerResult.HelperBitsSource += @($PSBoundParameters['Source'])
$PSBoundParameters['DisplayName'] = $Global:FdBitsTag
$PSBoundParameters['Description'] = $Global:FdBitsRun
& $Global:FdNativeBits @PSBoundParameters
}
}
$Result.Notes += 'Loaded helper functions copied into a fresh process; BITS display name/description tagged for cleanup; helper logs redirected to diagnostic scratch.'
$Result.HelperOutput = @(Download-File -URL $Context.URL -File $InputData.Destination -ErrorAction Stop 2>&1 | ForEach-Object { [string]$_ })
}
} elseif ($InputData.Method -eq 'BitsSync') {
Import-Module BitsTransfer -ErrorAction Stop
BitsTransfer\Start-BitsTransfer -Source $Context.URL -Destination $InputData.Destination -DisplayName $InputData.Tag -Description $Context.RunId -ErrorAction Stop
} elseif ($InputData.Method -eq 'BitsJob') {
Import-Module BitsTransfer -ErrorAction Stop
$Job = BitsTransfer\Start-BitsTransfer -Source $Context.URL -Destination $InputData.Destination -DisplayName $InputData.Tag -Description $Context.RunId -Asynchronous -ErrorAction Stop
$PreviousState = ''
while ($true) {
$Job = BitsTransfer\Get-BitsTransfer -JobId $Job.JobId -ErrorAction Stop
if ([string]$Job.JobState -ne $PreviousState) { Add-BitsSnapshot $Job 'State change'; $PreviousState = [string]$Job.JobState }
if (@('Transferred','Error','TransientError','Suspended','Cancelled','Acknowledged') -contains [string]$Job.JobState) { break }
Start-Sleep -Milliseconds 250
}
if ([string]$Job.JobState -ne 'Transferred') { throw ('BITS job ended probe in {0}: {1}' -f $Job.JobState,$Job.ErrorDescription) }
Add-BitsSnapshot $Job 'Before completion'
BitsTransfer\Complete-BitsTransfer -BitsJob $Job -ErrorAction Stop
} elseif ($InputData.Method -eq 'DotNetGet') {
$Response = $null; $InputStream = $null; $OutputStream = $null
try {
$Response = Open-HttpResponse 'GET' $false
$InputStream = $Response.GetResponseStream()
$OutputStream = [IO.File]::Open($InputData.Destination,[IO.FileMode]::Create,[IO.FileAccess]::Write,[IO.FileShare]::Read)
$Buffer = New-Object byte[] 65536
$Received = [long]0
while (($Count = $InputStream.Read($Buffer,0,$Buffer.Length)) -gt 0) { $OutputStream.Write($Buffer,0,$Count); $Received += $Count }
if ($Response.ContentLength -ge 0 -and $Received -ne $Response.ContentLength) { throw "HTTP body length mismatch: expected $($Response.ContentLength), received $Received." }
} finally {
if ($OutputStream) { $OutputStream.Dispose() }
if ($InputStream) { $InputStream.Dispose() }
if ($Response) { $Response.Close() }
}
}
if ($Result.Status -ne 'Skipped') {
$Result.Snapshots += Get-FileSnapshot 'After transfer'
Save-Checkpoint
Start-Sleep -Milliseconds 1000
$Result.Snapshots += Get-FileSnapshot 'One second later'
$After = $Result.Snapshots[$Result.Snapshots.Count-1]
$Result.Status = $(if ($After.Error) { 'Failed' } elseif (!$After.Exists) { 'MissingFile' } elseif ($After.Length -eq 0) { 'EmptyFile' } else { 'Complete' })
}
}
} catch {
$Result.Status = 'Failed'
$Result.Errors += Get-ErrorDetail $_
if ($InputData.Destination) { $Result.Snapshots += Get-FileSnapshot 'After error' }
} finally {
Save-Checkpoint
if (@('Helper','BitsSync','BitsJob') -contains $InputData.Method) { Remove-OwnedBitsJobs }
Save-Checkpoint
}
}
$FdWorkerPath = Join-Path $FdWork 'worker.ps1'
[IO.File]::WriteAllText($FdWorkerPath,$FdWorker.ToString(),[Text.Encoding]::UTF8)
# Capture both child pipes concurrently without PowerShell callbacks on foreign
# threads. .NET file APIs also avoid Start-Process's wildcard path expansion.
if (!('EmberkomDownloadDiagnostic.ChildRunner' -as [type])) {
Add-Type -TypeDefinition @'
using System;
using System.Diagnostics;
using System.IO;
namespace EmberkomDownloadDiagnostic {
public class ChildResult { public bool TimedOut; public int ExitCode; }
public static class ChildRunner {
public static ChildResult Run(string exe,string args,string directory,string stdout,string stderr,int milliseconds) {
using (StreamWriter output=new StreamWriter(stdout))
using (StreamWriter error=new StreamWriter(stderr))
using (Process child=new Process()) {
child.StartInfo.FileName=exe; child.StartInfo.Arguments=args;
child.StartInfo.WorkingDirectory=directory;
child.StartInfo.UseShellExecute=false; child.StartInfo.CreateNoWindow=true;
child.StartInfo.WindowStyle=ProcessWindowStyle.Hidden;
child.StartInfo.RedirectStandardOutput=true; child.StartInfo.RedirectStandardError=true;
child.OutputDataReceived+=delegate(object sender,DataReceivedEventArgs e) { if(e.Data!=null) lock(output) { output.WriteLine(e.Data); } };
child.ErrorDataReceived+=delegate(object sender,DataReceivedEventArgs e) { if(e.Data!=null) lock(error) { error.WriteLine(e.Data); } };
child.Start(); child.BeginOutputReadLine(); child.BeginErrorReadLine();
ChildResult result=new ChildResult();
result.TimedOut=!child.WaitForExit(milliseconds);
if(result.TimedOut) {
try { child.Kill(); } catch(InvalidOperationException) { }
if(!child.WaitForExit(5000)) throw new TimeoutException("Diagnostic child did not exit after termination.");
}
child.WaitForExit(); // Process has exited; drain the asynchronous pipe callbacks.
result.ExitCode=child.ExitCode;
return result;
}
}
}
}
'@
}
function Invoke-FdWorker($Spec, $Limit) {
$InputFile = Join-Path $FdWork ($Spec.Name + '-input.clixml')
Export-FdXml $Spec $InputFile 8
$Code = '& ([scriptblock]::Create([IO.File]::ReadAllText(''' + $FdWorkerPath.Replace("'","''") + '''))) ''' + $InputFile.Replace("'","''") + ''''
$Encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($Code))
$Arguments = '-NoProfile -NonInteractive '
if ($PSVersionTable.PSVersion.Major -eq 2) { $Arguments += '-Version 2.0 ' }
$Arguments += '-EncodedCommand ' + $Encoded
$Child = [EmberkomDownloadDiagnostic.ChildRunner]::Run($FdExe,$Arguments,$FdContext.WorkingDirectory,(Join-Path $FdWork ($Spec.Name + '-stdout.txt')),(Join-Path $FdWork ($Spec.Name + '-stderr.txt')),($Limit * 1000))
$TimedOut = $Child.TimedOut
$ExitCode = $Child.ExitCode
$Item = $null
if (Test-Path -LiteralPath $Spec.ResultPath) {
try { $Item = Import-Clixml -Path ([Management.Automation.WildcardPattern]::Escape($Spec.ResultPath)) } catch { $Item = $null }
}
if (!$Item) { $Item = New-Object PSObject -Property @{Name=$Spec.Name; Method=$Spec.Method; ExistingEmpty=$Spec.ExistingEmpty; Status='Failed'; Notes=@('Child did not produce a readable result.'); Errors=@(); Snapshots=@(); CleanupErrors=@(); ElapsedSeconds=$Limit} }
if ($TimedOut) { $Item.Status = 'TimedOut'; $Item.Notes += "Stopped after $Limit seconds; partial checkpoint retained." }
elseif (($null -ne $ExitCode -and $ExitCode -ne 0) -or $Item.Status -eq 'Running') { $Item.Status = 'Failed' }
foreach ($StreamName in @('stdout','stderr')) {
$StreamPath = Join-Path $FdWork ($Spec.Name + '-' + $StreamName + '.txt')
$StreamText = $(if (Test-Path -LiteralPath $StreamPath) { [IO.File]::ReadAllText($StreamPath) } else { '' })
Add-Member -InputObject $Item -MemberType NoteProperty -Name $StreamName -Value $StreamText
}
Add-Member -InputObject $Item -MemberType NoteProperty -Name ChildExitCode -Value $ExitCode
return $Item
}
$FdReport = New-Object PSObject -Property @{
SchemaVersion=1; RunId=$FdRunId; StartedUtc=[DateTime]::UtcNow.ToString('o'); Context=$FdContext
Probes=@(); Cleanup=@(); Findings=@(); ReportDirectory=$FdRoot
Limitations=@(
'Fresh child processes reproduce the caller TLS selection but not existing pooled connections, custom certificate callbacks, or custom in-memory proxy objects.',
'Registry paths reflect this process registry view; a 32-bit process may not expose the native 64-bit view.',
'Helper dependencies outside the captured functions/global LogFile are not reproduced.',
'Scratch files preserve the original URL extension when usable, but not the production path; path-specific security behavior may differ.',
'PowerShell 2-4 compatibility requires actual runtime validation; newer features elsewhere in Tools.ps1 are separate from this diagnostic.'
)
}
$FdReportXml = Join-Path $FdRoot 'report.clixml'
$FdReportText = Join-Path $FdRoot 'report.txt'
try {
$FdCases = @('Environment','Headers','Helper','BitsSync','BitsJob','DotNetGet')
foreach ($FdMethod in $FdCases) {
$FdVariants = @($false)
if (@('Helper','BitsSync','BitsJob','DotNetGet') -contains $FdMethod) { $FdVariants = @($false,$true) }
foreach ($FdExisting in $FdVariants) {
$FdName = $FdMethod + $(if ($FdExisting) { '-existing-empty' } else { '-new' })
$FdSpec = New-Object PSObject -Property @{
Name=$FdName; Method=$FdMethod; ExistingEmpty=$FdExisting; ContextPath=$FdContextPath
ResultPath=(Join-Path $FdWork ($FdName + '-result.clixml'))
Destination=(Join-Path $FdWork ($FdName + $FdExtension))
LogPath=(Join-Path $FdWork ($FdName + '-helper.log'))
Tag=('Emberkom-DownloadDiagnostic-' + $FdRunId + '-' + $FdName)
}
Write-Host ("Testing {0} (limit {1}s)..." -f $FdName,$FdTimeout)
$FdResult = Invoke-FdWorker $FdSpec $FdTimeout
if (Test-Path -LiteralPath $FdSpec.LogPath) { Add-Member -InputObject $FdResult -MemberType NoteProperty -Name HelperLog -Value ([IO.File]::ReadAllText($FdSpec.LogPath)) }
$FdReport.Probes += $FdResult
Export-FdXml $FdReport $FdReportXml 20
# A killed child cannot run finally. Always use a separate, bounded
# cleanup worker; filter by BOTH unpredictable run ID and exact tag.
if (@('Helper','BitsSync','BitsJob') -contains $FdMethod) {
$FdSpec.Method = 'Cleanup'
$FdSpec.Name += '-cleanup'
$FdSpec.ResultPath = Join-Path $FdWork ($FdSpec.Name + '-result.clixml')
$FdReport.Cleanup += Invoke-FdWorker $FdSpec 15
}
}
}
$FdDownloads = @($FdReport.Probes | Where-Object { @('Helper','BitsSync','BitsJob','DotNetGet') -contains $_.Method -and $_.Status -ne 'Skipped' })
$FdBad = @($FdDownloads | Where-Object { $_.Status -ne 'Complete' })
$FdHashes = @($FdDownloads | Where-Object { $_.Status -eq 'Complete' } | ForEach-Object { $_.Snapshots[$_.Snapshots.Count-1].SHA256 } | Sort-Object -Unique)
if ($FdBad.Count -eq 0 -and $FdHashes.Count -eq 1) {
$FdReport.Findings += 'All tested download paths produced the same nonempty file. Failure not reproduced; no transport replacement is justified by this run.'
} else {
$FdReport.Findings += 'A failure or content difference was observed. Compare per-probe errors, HTTP responses, BITS byte counts, and file snapshots before choosing a shared fix.'
}
foreach ($FdProbe in $FdDownloads) {
$FdAfter = @($FdProbe.Snapshots | Where-Object { $_.Label -eq 'After transfer' -or $_.Label -eq 'One second later' })
if ($FdAfter.Count -eq 2 -and ($FdAfter[0].SHA256 -ne $FdAfter[1].SHA256 -or $FdAfter[0].Length -ne $FdAfter[1].Length)) {
$FdReport.Findings += "File changed after transfer in $($FdProbe.Name); investigate post-download modification."
}
}
} finally {
# This directory is created exclusively by this invocation. Delete only its
# immediate scratch files; never recurse or touch the production destination.
$FdResolvedWork = (Resolve-Path -LiteralPath $FdWork).Path
$FdExpectedWork = [IO.Path]::GetFullPath((Join-Path $FdRoot 'work'))
if ($FdResolvedWork -ne $FdExpectedWork -or (Get-Item -LiteralPath $FdResolvedWork).Attributes -band [IO.FileAttributes]::ReparsePoint) {
$FdReport.Findings += 'Scratch path validation failed; cleanup skipped.'
} else {
foreach ($FdScratch in @(Get-ChildItem -LiteralPath $FdResolvedWork -Force)) {
if (!$FdScratch.PSIsContainer) {
try { Remove-Item -LiteralPath $FdScratch.FullName -Force -ErrorAction Stop }
catch { $FdReport.Findings += ('Could not remove scratch file: ' + $FdScratch.FullName + ': ' + $_.Exception.Message) }
}
}
if (@(Get-ChildItem -LiteralPath $FdResolvedWork -Force).Count -eq 0) { Remove-Item -LiteralPath $FdResolvedWork -Force }
}
Export-FdXml $FdReport $FdReportXml 20
# A readable recursive rendering retains nested exception and HTTP details.
function Format-FdReport($Value, $Indent) {
if ($null -eq $Value) { return ($Indent + '<null>') }
if ($Value -is [string] -or $Value -is [ValueType]) { return ($Indent + [string]$Value) }
if ($Value -is [Collections.IDictionary]) {
foreach ($Key in @($Value.Keys | Sort-Object)) { $Indent + [string]$Key + ':'; Format-FdReport $Value[$Key] ($Indent + ' ') }
} elseif ($Value -is [Collections.IEnumerable]) {
foreach ($Entry in $Value) { Format-FdReport $Entry ($Indent + ' ') }
} else {
foreach ($Property in $Value.PSObject.Properties) {
if (@('NoteProperty','Property') -contains [string]$Property.MemberType) { $Indent + $Property.Name + ':'; Format-FdReport $Property.Value ($Indent + ' ') }
}
}
}
$FdText = @(Format-FdReport $FdReport '') -join [Environment]::NewLine
[IO.File]::WriteAllText($FdReportText,$FdText,[Text.Encoding]::UTF8)
}
Write-Host ("Diagnostic reports: {0}" -f $FdRoot)
New-Object PSObject -Property @{TextReport=$FdReportText; XmlReport=$FdReportXml; Findings=$FdReport.Findings}
} $URL $OutputDirectory $TimeoutSeconds
+16 -16
View File
@@ -7,27 +7,27 @@ $ZIPFILE = Download-File -URL $URL
## Setup the directory to extract the downloaded zip file to
$WORKINGDIR = $ZIPFILE.Substring(0, $ZIPFILE.LastIndexOf('.'))
If ( Test-Path $WORKINGDIR ) {
LogMsg "Deleting existing directory: ""$WORKINGDIR"""
Write-Output "Deleting existing directory: ""$WORKINGDIR"""
Try { Remove-Item -Path $WORKINGDIR -Recurse -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
LogMsg "Creating new directory: ""$WORKINGDIR"""
Write-Output "Creating new directory: ""$WORKINGDIR"""
Try { New-Item -Path $WORKINGDIR -ItemType Directory -Force | Out-Null }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Extract $ZIPFILE to $WORKINGDIR
Try {
Add-Type -Assembly System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::ExtractToDirectory($ZIPFILE, $WORKINGDIR)
}
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
## Run the SpeedTest CLI app
$SPEEDTEST = "${WORKINGDIR}\speedtest.exe"
If ( Test-Path $SPEEDTEST ) {
Try {
LogMsg "Starting internet bandwidth test"
Write-Output "Starting internet bandwidth test"
$RESULTS = [string]( cmd.exe /c ""${SPEEDTEST} --accept-license --progress=no"" )
## Cleanup the output
@@ -41,22 +41,22 @@ If ( Test-Path $SPEEDTEST ) {
$RESULTS = $RESULTS -replace "Result URL: ", "`nResult URL: "
$RESULTS = $RESULTS + "`n"
} Else { $RESULTS = "The test did not product any output" }
LogMsg $RESULTS
LogMsg "Finished internet bandwidth test"
Write-Output $RESULTS
Write-Output "Finished internet bandwidth test"
}
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
} Else { LogMsg "The file does not exist at the expected path: ""${SPEEDTEST}""" }
} Else { Write-Output "The file does not exist at the expected path: ""${SPEEDTEST}""" }
## Cleanup files and directories created by this script
If ( Test-Path $ZIPFILE ) {
LogMsg "Deleting downloaded zip file: ""$ZIPFILE"""
Write-Output "Deleting downloaded zip file: ""$ZIPFILE"""
Try { Remove-Item -Path $ZIPFILE -Force }
Catch { LogErr $_.Exception.Message }
} Else { LogMsg "Downloaded zip file no longer exists: ""$ZIPFILE""" }
Catch { Write-Error $_.Exception.Message }
} Else { Write-Output "Downloaded zip file no longer exists: ""$ZIPFILE""" }
If ( Test-Path $WORKINGDIR ) {
LogMsg "Deleting directory from extracted zip file: ""$WORKINGDIR"""
Write-Output "Deleting directory from extracted zip file: ""$WORKINGDIR"""
Try { Remove-Item -Path $WORKINGDIR -Recurse -Force }
Catch { LogErr $_.Exception.Message }
} Else { LogMsg "Directory from extracted zip file no longer exists: ""$WORKINGDIR""" }
Catch { Write-Error $_.Exception.Message }
} Else { Write-Output "Directory from extracted zip file no longer exists: ""$WORKINGDIR""" }
+1210 -252
View File
File diff suppressed because it is too large Load Diff
+147
View File
@@ -0,0 +1,147 @@
# Specify the working directory for this script to place files
$WORK_DIR = "${Env:ProgramData}\Atera"
# Specify the name of the previous generation file to compare with the current generation file
$P_FILE = "${WORK_DIR}\InstalledSoftware_Previous.csv"
# Specify the name of the current generation file to compare with the previous generation file
$C_FILE = "${WORK_DIR}\InstalledSoftware_Current.csv"
# Function to append a line to a file
Function Write-LineToFile {
param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Line)
If ( -not (Test-Path $Path) ) {
#Write-Output "Creating file ""${Path}"""
Try { New-Item -Path $Path -ItemType File -Force | Out-Null }
Catch { Write-Error $_.Exception.Message }
}
Try { Add-Content -Path $Path -Value $Line }
Catch { Write-Error $_.Exception.Message }
}
# Function to test whether or not an item has a specified property
Function Test-ItemProperty {
param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Name)
Try {
$test = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue
If (($null -eq $test) -or ($test.Length -eq 0)) { Return $false } Else { Return $true }
}
Catch { Return $false }
}
# Function to retrieve an item property
Function Get-ItemPropertyInfo {
param([Parameter(Mandatory=$true)][string]$Path,[Parameter(Mandatory=$true)][string]$Name)
If ( Test-ItemProperty -Path $Path -Name $Name ) {
$retval = Get-ItemPropertyValue -Path $Path -Name $Name
$retval = $retval -replace ",", " "
# Additional processing for specific properties
Switch ( $Name ) {
# Convert the estimated size into MB
"EstimatedSize" {
$size = [int]$retval
$size = [Math]::Ceiling($size / 1024)
$retval = [string]$size
}
# Convert the date format into YYYY-MM-DD
"InstallDate" {
$origRetval = $retval
# Try to find each type of date string so we can convert it
Switch -Regex ( $retval ) {
'^\d{8}$' { $formatstring = 'yyyyMMdd' }
'^\d{1,2}\\\d{1,2}\\\d{4}$' { $formatstring = 'MM\dd\yyyy' }
'^\d{4}\\\d{1,2}\\\d{1,2}$' { $formatstring = 'yyyy\MM\dd' }
Default { $formatstring = $null }
}
# Make sure $formatstring has a value, if not just try to cast directly to a datetime
Try {
If ( $null -eq $formatstring ) {
$date = [datetime]$retval
} Else {
$date = [datetime]::ParseExact($retval,$formatstring,$null)
}
# Set $retval to a standard datetime format
$retval = $date.ToString('yyyy-MM-dd')
}
Catch { Write-Error $_.Exception.Message; $retval = $origRetval }
}
Default {}
}
}
Else { $retval = "" }
Return $retval
}
# Create $WORK_DIR if it doesn't exist
If ( -not (Test-Path $WORK_DIR) ) { New-Item -Path $WORK_DIR -ItemType Directory -Force | Out-Null }
# Create $C_FILE if it doesn't exist
If ( -not (Test-Path $C_FILE) ) {
# Write the headers to the output file
Write-LineToFile -Path $C_FILE -Line "Name,Publisher,Version,InstallDate,EstimatedSize(MB),UninstallString,ModifyPath"
# Iterate through all Registry Uninstall keys and build the CSV file of all installed software
ForEach ( $item in $(Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall','HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall') ) {
$DisplayName = Get-ItemPropertyInfo -Path $item.PSPath -Name DisplayName
$DisplayVersion = Get-ItemPropertyInfo -Path $item.PSPath -Name DisplayVersion
$EstimatedSize = Get-ItemPropertyInfo -Path $item.PSPath -Name EstimatedSize
$InstallDate = Get-ItemPropertyInfo -Path $item.PSPath -Name InstallDate
$Publisher = Get-ItemPropertyInfo -Path $item.PSPath -Name Publisher
$UninstallString = Get-ItemPropertyInfo -Path $item.PSPath -Name UninstallString
$ModifyPath = Get-ItemPropertyInfo -Path $item.PSPath -Name ModifyPath
# Write installed software to output file only if it has a name and uninstall/modify path, otherwise discard
If ( -not (($DisplayName -eq "") -and ($UninstallString -eq "") -and ($ModifyPath -eq "")) ) {
$content = "${DisplayName},${Publisher},${DisplayVersion},${InstallDate},${EstimatedSize},${UninstallString},${ModifyPath}"
Write-LineToFile -Path $C_FILE -Line $content
}
}
}
# Compare $C_FILE and $P_FILE
If ( (Test-Path $C_FILE) -and (Test-Path $P_FILE) ) {
$current = Import-Csv -Path $C_FILE
$previous = Import-Csv -Path $P_FILE
# Items that exist in $C_FILE but not $P_FILE have been installed
$installed = Compare-Object -ReferenceObject $current -DifferenceObject $previous -Property Name,Version -PassThru | Where-Object { (![string]::IsNullOrEmpty($_.Name)) -and ($_.SideIndicator -eq '<=') }
# Items that exist in $P_FILE but not $C_FILE have been uninstalled
$uninstalled = Compare-Object -ReferenceObject $current -DifferenceObject $previous -Property Name,Version -PassThru | Where-Object { (![string]::IsNullOrEmpty($_.Name)) -and ($_.SideIndicator -eq '=>') }
# Alert trigger for Atera
If ( $installed -or $uninstalled ) { Write-Output "Application change detected" }
# Display installed apps
If ( $installed ) {
Write-Output "`nAPPLICATIONS RECENTLY INSTALLED:"
$installed | Select-Object * -ExcludeProperty SideIndicator
}
# Display uninstalled apps
If ( $uninstalled ) {
Write-Output "`nAPPLICATIONS RECENTLY UNINSTALLED:"
$uninstalled | Select-Object * -ExcludeProperty UninstallString,ModifyPath,SideIndicator
}
}
# Delete $P_FILE
If ( Test-Path $P_FILE ) {
#Write-Output "Deleting ""${P_FILE}"""
Remove-Item -Path $P_FILE -Force
}
# Rename $C_FILE to $P_FILE
If ( Test-Path $C_FILE ) {
#Write-Output "Renaming ""${C_FILE}"" to ""${P_FILE}"""
Move-Item -Path $C_FILE -Destination $P_FILE -Force
}
+5 -5
View File
@@ -6,13 +6,13 @@ If ( IsAdmin )
{ $RegKey = 'HKLM:SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Adobe Flash Player NPAPI' }
If ( $RegKey )
{
LogMsg "Found uninstall string: ${UninstallString}"
Write-Output "Found uninstall string: ${UninstallString}"
$UninstallString = (Get-ItemProperty -Path $RegKey).UninstallString
$UninstallProgram = $UninstallString -replace ' -maintain plugin', ''
LogMsg "Running: ""${UninstallProgram}"" -uninstall"
Write-Output "Running: ""${UninstallProgram}"" -uninstall"
Try { Start-Process "${UninstallProgram}" -ArgumentList "-uninstall" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
Else { LogMsg "Adobe Flash Player is not installed" }
Else { Write-Output "Adobe Flash Player is not installed" }
}
Else { LogMsg "Must have administrative privileges to uninstall Adobe Flash" }
Else { Write-Output "Must have administrative privileges to uninstall Adobe Flash" }
+24 -24
View File
@@ -8,7 +8,7 @@ Function Get-UninstallCodes ([string]$DisplayName) {
If ( $(Get-ItemProperty -Path $_.PSPath -Name 'DisplayName' -ErrorAction SilentlyContinue) -and ($(Get-ItemPropertyValue -Path $_.PSPath -Name 'DisplayName' -ErrorAction SilentlyContinue) -eq $DisplayName) ) {
$str = (Get-ItemPropertyValue -Path $_.PSPath -Name 'UninstallString')
$code = $str.Substring(($str.Length - 37),36)
LogMsg " - ${code}"
Write-Output " - ${code}"
$UninstallCodes.Add($code) | Out-Null
}
}
@@ -19,7 +19,7 @@ Function Get-ProductKeys ([string]$ProductName) {
Get-ChildItem -Path 'HKCR:Installer\Products' | ForEach-Object {
If ( $(Get-ItemProperty -Path $_.PSPath -Name 'ProductName' -ErrorAction SilentlyContinue) -and ($(Get-ItemPropertyValue -Path $_.PSPath -Name 'ProductName' -ErrorAction SilentlyContinue) -eq $ProductName) ) {
$prod = $_.PSPath.Substring($_.PSPath.Length - 32)
LogMsg " - ${prod}"
Write-Output " - ${prod}"
$ProductKeys.Add($prod) | Out-Null
}
}
@@ -28,30 +28,30 @@ Function Get-ProductKeys ([string]$ProductName) {
Function Get-ServiceStatus ([string]$Name) { (Get-Service -Name $Name -ErrorAction SilentlyContinue).Status }
Function Stop-RunningService ([string]$Name) {
If ( $(Get-ServiceStatus -Name $Name) -eq "Running" ) { LogMsg "Stopping : ${Name} service" ; Stop-Service -Name $Name -Force }
If ( $(Get-ServiceStatus -Name $Name) -eq "Running" ) { Write-Output "Stopping : ${Name} service" ; Stop-Service -Name $Name -Force }
}
Function Remove-StoppedService ([string]$Name) {
$s = (Get-ServiceStatus -Name $Name)
If ( $s ) {
If ( $s -eq "Stopped" ) {
LogMsg "Deleting : ${Name} service"
Write-Output "Deleting : ${Name} service"
Start-Process "sc.exe" -ArgumentList "delete ${Name}" -Wait
}
} Else { LogMsg "Not Found: ${Name} service" }
} Else { Write-Output "Not Found: ${Name} service" }
}
Function Stop-RunningProcess ([string]$Name) {
$p = (Get-Process -Name $_ -ErrorAction SilentlyContinue)
If ( $p ) { LogMsg "Stopping : ${Name}.exe" ; $p | Stop-Process -Force }
Else { LogMsg "Not Found: ${Name}.exe is not running"}
If ( $p ) { Write-Output "Stopping : ${Name}.exe" ; $p | Stop-Process -Force }
Else { Write-Output "Not Found: ${Name}.exe is not running"}
}
Function Remove-Path ([string]$Path) {
If ( Test-Path $Path ) {
LogMsg "Deleting : ${Path}"
Write-Output "Deleting : ${Path}"
Remove-Item $Path -Recurse -Force
} Else { LogMsg "Not Found: ${Path}" }
} Else { Write-Output "Not Found: ${Path}" }
}
Function Get-AllExeFiles ([string]$Path) {
@@ -60,39 +60,39 @@ Function Get-AllExeFiles ([string]$Path) {
}
}
LogMsg "Uninstalling Atera agent"
Write-Output "Uninstalling Atera agent"
LogMsg "Mounting HKEY_CLASSES_ROOT registry hive"
Write-Output "Mounting HKEY_CLASSES_ROOT registry hive"
New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null
LogMsg "Getting MSI package codes from the uninstall key"
Write-Output "Getting MSI package codes from the uninstall key"
$UninstallCodes = New-Object System.Collections.ArrayList
'AteraAgent' | ForEach-Object { Get-UninstallCodes -DisplayName $_ }
LogMsg "Getting product keys from the list of installed products"
Write-Output "Getting product keys from the list of installed products"
$ProductKeys = New-Object System.Collections.ArrayList
'AteraAgent' | ForEach-Object { Get-ProductKeys -ProductName $_ }
LogMsg "Directories that we may need to delete"
Write-Output "Directories that we may need to delete"
$Directories = @("${Env:ProgramFiles}\ATERA Networks")
$Directories | ForEach-Object { LogMsg " - ${_}" }
$Directories | ForEach-Object { Write-Output " - ${_}" }
LogMsg "Processes that we may need to terminate"
Write-Output "Processes that we may need to terminate"
$ExeFiles = New-Object System.Collections.ArrayList
$Directories | ForEach-Object { Get-AllExeFiles -Path $_ }
$ExeFiles.Add('reg') | Out-Null
$ExeFiles | ForEach-Object { LogMsg " - ${_}.exe" }
$ExeFiles | ForEach-Object { Write-Output " - ${_}.exe" }
LogMsg "Windows services that we may need to stop and/or delete"
Write-Output "Windows services that we may need to stop and/or delete"
$ServiceList = @('AteraAgent')
$ServiceList | ForEach-Object { LogMsg " - ${_}" }
$ServiceList | ForEach-Object { Write-Output " - ${_}" }
LogMsg "Windows Registry keys we may need to delete"
Write-Output "Windows Registry keys we may need to delete"
$RegistryKeys = @('HKLM:SOFTWARE\ATERA Networks')
$RegistryKeys | ForEach-Object { LogMsg " - ${_}" }
$RegistryKeys | ForEach-Object { Write-Output " - ${_}" }
# Uninstall each MSI package code in $UninstallCodes
$UninstallCodes | ForEach-Object { LogMsg "Uninstall: ${_}" ; Start-Process "msiexec.exe" -ArgumentList "/X{${_}} /qn" -Wait }
$UninstallCodes | ForEach-Object { Write-Output "Uninstall: ${_}" ; Start-Process "msiexec.exe" -ArgumentList "/X{${_}} /qn" -Wait }
# Stop services if they're still running
$ServiceList | ForEach-Object { Stop-RunningService -Name $_ }
@@ -112,10 +112,10 @@ $RegistryKeys | ForEach-Object { Remove-Path -Path $_ }
# Delete remaining directories
$Directories | ForEach-Object { Remove-Path -Path $_ }
LogMsg "Unmount HKEY_CLASSES_ROOT registry hive"
Write-Output "Unmount HKEY_CLASSES_ROOT registry hive"
Remove-PSDrive -Name HKCR
LogMsg "Finished uninstalling Atera Agent"
Write-Output "Finished uninstalling Atera Agent"
# Delete this script
Remove-Item $MyInvocation.MyCommand.Path -Force
+13 -13
View File
@@ -6,38 +6,38 @@ If ( $PATH )
$SVC = Get-Service -Name "AdAppMgrSvc"
If ( $SVC.Status -ne "Stopped" )
{
LogMsg "Stopping AdAppMgrSvc service"
Write-Output "Stopping AdAppMgrSvc service"
Try { $SVC | Stop-Service -Force }
Catch { LogMsg $_.Exception.Message }
Catch { Write-Output $_.Exception.Message }
}
LogMsg "Stopping processes"
Write-Output "Stopping processes"
Try
{
Get-Process | Where { $_.Name -like "AdAppMgr" } | Stop-Process -Force
Get-Process | Where { $_.Name -like "AutodeskDesktopApp" } | Stop-Process -Force
Get-Process | Where-Object { $_.Name -like "AdAppMgr" } | Stop-Process -Force
Get-Process | Where-Object { $_.Name -like "AutodeskDesktopApp" } | Stop-Process -Force
}
Catch { LogMsg $_.Exception.Message }
Catch { Write-Output $_.Exception.Message }
LogMsg "Uninstalling Autodesk Desktop App"
Write-Output "Uninstalling Autodesk Desktop App"
Try { Start-Process -FilePath $PATH -ArgumentList '--mode unattended' -Wait }
Catch { LogMsg $_.Exception.Message }
Catch { Write-Output $_.Exception.Message }
If ( Test-Path "HKLM:\SOFTWARE\Wow6432Node\Autodesk\Autodesk Application Manager" ) {
LogMsg "Removing: ""HKLM:\SOFTWARE\Wow6432Node\Autodesk\Autodesk Application Manager"""
Write-Output "Removing: ""HKLM:\SOFTWARE\Wow6432Node\Autodesk\Autodesk Application Manager"""
Remove-Item "HKLM:\SOFTWARE\Wow6432Node\Autodesk\Autodesk Application Manager" -Force -ErrorAction SilentlyContinue
}
If ( Test-Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Autodesk Desktop App" ) {
LogMsg "Removing: ""HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Autodesk Desktop App"""
Write-Output "Removing: ""HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Autodesk Desktop App"""
Remove-Item "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Autodesk Desktop App" -Force -ErrorAction SilentlyContinue
}
If ( Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AdAppMgrSvc.exe" ) {
LogMsg "Removing: ""HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AdAppMgrSvc.exe"""
Write-Output "Removing: ""HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AdAppMgrSvc.exe"""
Remove-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AdAppMgrSvc.exe" -Force -ErrorAction SilentlyContinue
}
If ( Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AutodeskDesktopApp.exe" ) {
LogMsg "Removing: ""HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AutodeskDesktopApp.exe"""
Write-Output "Removing: ""HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AutodeskDesktopApp.exe"""
Remove-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\AutodeskDesktopApp.exe" -Force -ErrorAction SilentlyContinue
}
}
Else { LogMsg "Autodesk Desktop App is not installed" }
Else { Write-Output "Autodesk Desktop App is not installed" }
+5 -6
View File
@@ -1,8 +1,7 @@
## Install and import additional Powershell modules
Install-MSP360Module
Import-Module -Name MSP360
# Install and import additional Powershell modules
Import-MSP360Module
## Uninstall the agent
LogMsg "Uninstalling Emberkom Cloud Backup agent"
# Uninstall the agent
Write-Output "Uninstalling Emberkom Cloud Backup agent"
Try { Remove-MBSAgent -RemoveSettings -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
-49
View File
@@ -1,49 +0,0 @@
# $APP_NAME must be set by the calling script or specified here
# Get all of the uninstall registry keys
$UNINSTALL_KEYS = Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
$UNINSTALL_KEYS += Get-ChildItem -Path HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
Function Get-GUIDFromMSIUninstallString {
param([string]$str)
$start = $str.IndexOf('{') + 1
$end = $str.IndexOf('}')
Return $str.Substring($start, $end - $start)
}
Foreach ( $reg in $UNINSTALL_KEYS ) {
# Get the uninstall key
$REG_PROPERTY = Get-ItemProperty -Path $reg.PSPath
# Get the display name of the app
$DISPLAY_NAME = $REG_PROPERTY.DisplayName
# Get the UninstallString
$UNINSTALLSTRING = $REG_PROPERTY.UninstallString
# Get the ModifyPath
$MODIFYPATH = $REG_PROPERTY.ModifyPath
# Make sure the DisplayName matches what we're looking for
If ( $DISPLAY_NAME -ilike $APP_NAME ) {
# Make sure the software was installed using an MSI
If ( $REG_PROPERTY.WindowsInstaller -eq 1 ) {
# Get the product code from the UninstallString
If ( $UNINSTALLSTRING ) { $PRODUCT_CODE = $(Get-GUIDFromMSIUninstallString -str $UNINSTALLSTRING) }
# Get the product code from the ModifyPath
ElseIf ( $MODIFYPATH ) { $PRODUCT_CODE = $(Get-GUIDFromMSIUninstallString -str $MODIFYPATH) }
# Uninstall the app
If ( $PRODUCT_CODE ) {
LogMsg "Uninstalling ${DISPLAY_NAME} ${PRODUCT_CODE}"
$arguments = '/X{' + $PRODUCT_CODE + '} /qn /norestart'
Try { Start-Process -FilePath "msiexec.exe" -ArgumentList $arguments -Wait }
Catch { LogErr $_.Exception.Message }
}
}
Else {
LogMsg "Cannot uninstall ""${DISPLAY_NAME}""`n UninstallString: ${UNINSTALLSTRING}"
}
}
}
+26 -2
View File
@@ -1,3 +1,21 @@
# Close all Office apps
End-Process -Name 'POWERPNT' -Match
End-Process -Name 'WINWORD' -Match
End-Process -Name 'EXCEL' -Match
End-Process -Name 'OUTLOOK' -Match
End-Process -Name 'MSPUB' -Match
End-Process -Name 'MSACCESS' -Match
End-Process -Name 'Teams' -Match
Start-Sleep -Seconds 5
End-Process -Name 'POWERPNT' -Match -Force
End-Process -Name 'WINWORD' -Match -Force
End-Process -Name 'EXCEL' -Match -Force
End-Process -Name 'OUTLOOK' -Match -Force
End-Process -Name 'MSPUB' -Match -Force
End-Process -Name 'MSACCESS' -Match -Force
End-Process -Name 'Teams' -Match -Force
# URL to the OffscrubC2R.vbs script
$UninstallScriptURL = 'https://github.com/OfficeDev/Office-IT-Pro-Deployment-Scripts/raw/master/Office-ProPlus-Deployment/Deploy-OfficeClickToRun/OffScrubc2r.vbs'
@@ -8,6 +26,12 @@ $UninstallScript = Download-File -URL $UninstallScriptURL
$ScriptArgs = "/ALL /QUIET /NOCANCEL"
# Run the script
LogMsg "Uninstalling all Click2Run versions of Office"
Write-Output "Uninstalling all Click2Run versions of Office"
Try { Start-Process "cscript.exe" -ArgumentList "//nologo ""${UninstallScript}"" ${ScriptArgs}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
# Delete the downloaded script
If ( Test-Path $UninstallScript ) {
Write-Output "Deleting downloaded script: ""${UninstallScript}"""
Remove-Item -Path $UninstallScript -Force -ErrorAction SilentlyContinue
}
+30 -30
View File
@@ -1,38 +1,38 @@
## Remove N-Central Patch Management Service Controller
If ( Test-Path "${Env:ProgramFiles(x86)}\MspPlatform\PME\unins000.exe" ) {
LogMsg "Uninstalling N-Central Patch Management Service Controller"
Write-Output "Uninstalling N-Central Patch Management Service Controller"
Try { Start-Process "${Env:ProgramFiles(x86)}\MspPlatform\PME\unins000.exe" -ArgumentList "/SILENT" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove N-Central File Cache Service Agent
If ( Test-Path "${Env:ProgramFiles(x86)}\MspPlatform\FileCacheServiceAgent\unins000.exe" ) {
LogMsg "Uninstalling N-Central File Cache Service Agent"
Write-Output "Uninstalling N-Central File Cache Service Agent"
Try { Start-Process "${Env:ProgramFiles(x86)}\MspPlatform\FileCacheServiceAgent\unins000.exe" -ArgumentList "/SILENT" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove N-Central Request Handler Agent
If ( Test-Path "${Env:ProgramFiles(x86)}\MspPlatform\RequestHandlerAgent\unins000.exe" ) {
LogMsg "Uninstalling N-Central Request Handler Agent"
Write-Output "Uninstalling N-Central Request Handler Agent"
Try { Start-Process "${Env:ProgramFiles(x86)}\MspPlatform\RequestHandlerAgent\unins000.exe" -ArgumentList "/SILENT" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove Windows Software Probe
If ( Test-Path "${ProgramFiles(x86)}\N-able Technologies\Windows Software Probe\" ) {
$ProductCode = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\N-able Technologies\Windows Software Probe").ProductCode
LogMsg "Uninstalling Windows Software Probe"
Write-Output "Uninstalling Windows Software Probe"
Try { Start-Process "msiexec.exe" -ArgumentList "/X {${ProductCode}} /qn" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove Windows Agent
If ( Test-Path "${ProgramFiles(x86)}\N-able Technologies\Windows Agent\" ) {
LogMsg "Uninstalling N-Central Windows Agent"
Write-Output "Uninstalling N-Central Windows Agent"
Try { Start-Process "msiexec.exe" -ArgumentList "/X{F5873D07-85EE-4010-A461-B60989884B1C} /qn" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove NablePatchRepositoryService
@@ -40,9 +40,9 @@ $ServiceName = "NablePatchRepositoryService"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
If ( $Service.Length -gt 0 ) {
Control-Service -Stop -Name $ServiceName
LogMsg "Deleting the service: ""${ServiceName}"""
Write-Output "Deleting the service: ""${ServiceName}"""
Try { Start-Process "sc" -ArgumentList "delete ${ServiceName}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove Windows Software Probe Maintenance Service
@@ -50,9 +50,9 @@ $ServiceName = "Windows Software Probe Maintenance Service"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
If ( $Service.Length -gt 0 ) {
Control-Service -Stop -Name $ServiceName
LogMsg "Deleting the service: ""${ServiceName}"""
Write-Output "Deleting the service: ""${ServiceName}"""
Try { Start-Process "sc" -ArgumentList "delete ${ServiceName}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove Windows Software Probe Syslog Service
@@ -60,9 +60,9 @@ $ServiceName = "Windows Software Probe Syslog Service"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
If ( $Service.Length -gt 0 ) {
Control-Service -Stop -Name $ServiceName
LogMsg "Deleting the service: ""${ServiceName}"""
Write-Output "Deleting the service: ""${ServiceName}"""
Try { Start-Process "sc" -ArgumentList "delete ${ServiceName}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove Windows Software Probe Service
@@ -70,9 +70,9 @@ $ServiceName = "Windows Software Probe Service"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
If ( $Service.Length -gt 0 ) {
Control-Service -Stop -Name $ServiceName
LogMsg "Deleting the service: ""${ServiceName}"""
Write-Output "Deleting the service: ""${ServiceName}"""
Try { Start-Process "sc" -ArgumentList "delete ${ServiceName}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove Windows Agent Maintenance Service
@@ -80,9 +80,9 @@ $ServiceName = "Windows Agent Maintenance Service"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
If ( $Service.Length -gt 0 ) {
Control-Service -Stop -Name $ServiceName
LogMsg "Deleting the service: ""${ServiceName}"""
Write-Output "Deleting the service: ""${ServiceName}"""
Try { Start-Process "sc" -ArgumentList "delete ${ServiceName}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove Windows Agent Service
@@ -90,33 +90,33 @@ $ServiceName = "Windows Agent Service"
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
If ( $Service.Length -gt 0 ) {
Control-Service -Stop -Name $ServiceName
LogMsg "Deleting the service: ""${ServiceName}"""
Write-Output "Deleting the service: ""${ServiceName}"""
Try { Start-Process "sc" -ArgumentList "delete ${ServiceName}" -Wait }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove "MspPlatform" directory
If ( Test-Path "${Env:ProgramFiles(x86)}\MspPlatform" ) {
LogMsg "Forcefully removing ""${Env:ProgramFiles(x86)}\MspPlatform"""
Write-Output "Forcefully removing ""${Env:ProgramFiles(x86)}\MspPlatform"""
Try { Remove-Item "${Env:ProgramFiles(x86)}\MspPlatform" -Recurse -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove "N-able Technologies" directory
If ( Test-Path "${Env:ProgramFiles(x86)}\N-able Technologies" ) {
LogMsg "Forcefully removing ""${Env:ProgramFiles(x86)}\N-able Technologies"""
Write-Output "Forcefully removing ""${Env:ProgramFiles(x86)}\N-able Technologies"""
Try { Remove-Item "${Env:ProgramFiles(x86)}\N-able Technologies" -Recurse -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
## Remove "N-able Technologies" registry key
If ( Test-Path "HKLM:SOFTWARE\Wow6432Node\N-able Technologies" ) {
LogMsg "Forcefully removing ""HKLM:SOFTWARE\Wow6432Node\N-able Technologies"""
Write-Output "Forcefully removing ""HKLM:SOFTWARE\Wow6432Node\N-able Technologies"""
Try { Remove-Item "HKLM:SOFTWARE\Wow6432Node\N-able Technologies" -Recurse -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
If ( Test-Path "HKLM:SOFTWARE\N-able Technologies" ) {
LogMsg "Forcefully removing ""HKLM:SOFTWARE\N-able Technologies"""
Write-Output "Forcefully removing ""HKLM:SOFTWARE\N-able Technologies"""
Try { Remove-Item "HKLM:SOFTWARE\N-able Technologies" -Recurse -Force }
Catch { LogErr $_.Exception.Message }
Catch { Write-Error $_.Exception.Message }
}
+1 -1
View File
@@ -35,7 +35,7 @@ Else
}
# Kill all running instances of TeamViewer
Get-Process | Where { $_.Name -like "*TeamViewer*" } | Stop-Process -Force
Get-Process | Where-Object { $_.Name -like "*TeamViewer*" } | Stop-Process -Force
If ( Test-Path "${INSTALLDIR}\uninstall.exe" )
{ Start-Process "${INSTALLDIR}\uninstall.exe" -ArgumentList "/S" -Wait }
+41 -17
View File
@@ -1,5 +1,5 @@
# Make sure this script runs as admin
#If ( IsAdmin -eq $false ) { LogMsg "This script can only be run as admin" ; Exit }
#If ( IsAdmin -eq $false ) { Write-Output "This script can only be run as admin" ; Exit }
# This is the list of Windows 10 AppX app names that we'll try to uninstall
$AppList = @(
@@ -57,26 +57,50 @@ If ( IsWindowsBuild -ge 9200 )
$PackageFullName = $($(Get-AppxPackage $App).PackageFullName)
$ProPackageFullName = $($(Get-AppxProvisionedPackage -Online | Where-Object {$_.Displayname -eq $App}).PackageName)
# If the package exists, uninstall it
# Make sure we have a package
If ($PackageFullName) {
LogMsg “Removing package: ${PackageFullName}”
Try { Remove-AppxPackage -Package $PackageFullName }
Catch { LogErr $_.Exception.Message }
# Test if the package is an array
If ( $PackageFullName -is [System.Array] ) {
# Iterate through the array of packages and uninstall each one
ForEach ( $package in $PackageFullName ) {
Write-Output “Removing package: ${package}”
Try { Remove-AppxPackage -Package $package }
Catch { Write-Error $_.Exception.Message }
}
# If it's not an array, just uninstall the package
} Else {
Write-Output “Removing package: ${PackageFullName}”
Try { Remove-AppxPackage -Package $PackageFullName }
Catch { Write-Error $_.Exception.Message }
}
}
# If the provisioned package exists, uninstall it
# Make sure we have a provisioned package
If ($ProPackageFullName) {
LogMsg “Removing provisioned package: ${ProPackageFullName}”
Try { Remove-AppxProvisionedPackage -PackageName $ProPackageFullName -Online -AllUsers }
Catch { LogErr $_.Exception.Message }
# Test if the provisioned package is an array
If ( $ProPackageFullName -is [System.Array] ) {
# Iterate through the array of provisioned packages and uninstall each one
ForEach ( $propackage in $ProPackageFullName ) {
Write-Output “Removing provisioned package: ${propackage}”
Try { Remove-AppxProvisionedPackage -PackageName $propackage -Online -AllUsers }
Catch { Write-Error $_.Exception.Message }
}
# If it's not an array, just uninstall the provisioned package
} Else {
Write-Output “Removing provisioned package: ${ProPackageFullName}”
Try { Remove-AppxProvisionedPackage -PackageName $ProPackageFullName -Online -AllUsers }
Catch { Write-Error $_.Exception.Message }
}
}
}
} Else { LogMsg "Cannot remove Appx packages because this endpoint is not running Windows 8 or higher" }
} Else { Write-Output "Cannot remove Appx packages because this endpoint is not running Windows 8 or higher" }
# Uninstall Dell SupportAssist apps
$APP_NAME = "Dell*SupportAssist*"
Run-Script -LivePSScript Uninstall-MSI
# Uninstall Dell SupportAssist apps
$APP_NAME = "Dell Digital Delivery*"
Run-Script -LivePSScript Uninstall-MSI
# Uninstall unwanted Dell apps
'Dell SupportAssist*', 'Dell Digital Delivery*', 'Dell Optimizer*', 'Dell Peripheral*', 'Dell Power Manager*', 'DellOptimizerUI*' | Uninstall-MSI
+1
View File
@@ -0,0 +1 @@
"wireguard" | Uninstall-MSI -Match
+4 -4
View File
@@ -11,11 +11,11 @@ If (IsAdmin) {
## If the path to wireguard.exe is found, update the app
If ( -not [string]::IsNullOrEmpty($PATH) ) {
LogMsg "Updating Wireguard"
Write-Output "Updating Wireguard"
Try { Start-Process $PATH -ArgumentList "/update" -Wait }
Catch { LogErr $_.Exception.Message }
LogMsg "Finished updating Wireguard"
Catch { Write-Error $_.Exception.Message }
Write-Output "Finished updating Wireguard"
}
}
Else { LogMsg "You must be a local administrator to update Wireguard" }
Else { Write-Output "You must be a local administrator to update Wireguard" }
+5
View File
@@ -0,0 +1,5 @@
WEBROOT=/var/www/html
if [ -d "${WEBROOT}/wp-content/plugins/wp-fail2ban/filters.d" ]; then
echo "Updating fail2ban filters"
cp -a "${WEBROOT}/wp-content/plugins/wp-fail2ban/filters.d/. /etc/fail2ban/filter.d/ && /usr/bin/fail2ban-client reload
fi
-39
View File
@@ -1,39 +0,0 @@
If ( $LFCLI ) {
## Create the path to $FileToUpload and zip the specified directory if necessary
If ( $(Get-Item $DataToUpload) -is [System.IO.DirectoryInfo] ) {
$FileToUpload = '{0}\{1}.zip' -f $Global:OutputDirectory,$(Split-Path -Leaf $DataToUpload)
Zip-Object -path $DataToUpload -dest $FileToUpload
$DeleteAfterUpload = $true
}
Else {
$FileToUpload = $DataToUpload
$DeleteAfterUpload = $false
}
Install-PSAteraModule
Import-Module -Name PSAtera
Set-AteraAPIKey -APIKey $AteraAPIKey
$LFHost = $(Get-AteraCustomValue -ObjectType Customer -ObjectId 9 -FieldName 'LiquidFiles Host URL').ValueAsString
$FiledropURL = "${LFHost}/filedrop/cmd"
## Set the message properties for the file upload
$From = '{0}@{1}.net' -f $Env:COMPUTERNAME,$($Global:MSPName -replace " ","").ToLower()
$Subject = [System.IO.Path]::GetFileNameWithoutExtension($FileToUpload)
$Message = ""
If ( Test-Path $FileToUpload ) {
LogMsg "Uploading ""${FileToUpload}"""
If ( $DeleteAfterUpload ) {
Try { Start-Process "${LFCLI}" -ArgumentList "filedrop /url:${FiledropURL} /from:""${From}"" /subject:""${Subject}"" /msg:""${Message}"" /c:n /deleteAfterUpload /f:""${FileToUpload}""" -Wait }
Catch { LogErr $_.Exception.Message }
}
Else {
Try { Start-Process "${LFCLI}" -ArgumentList "filedrop /url:${FiledropURL} /from:""${From}"" /subject:""${Subject}"" /msg:""${Message}"" /c:n /f:""${FileToUpload}""" -Wait }
Catch { LogErr $_.Exception.Message }
}
}
Else { LogMsg "The specified file does not exist: ""${FileToUpload}""" }
}
Else { LogMsg "Could not upload any data because the LiquidFiles CLI tool was not found" }
+85
View File
@@ -0,0 +1,85 @@
# File download diagnosis
`Test-FileDownload.ps1` compares the loaded `Download-File` helper, synchronous BITS, an inspectable BITS job, and a direct .NET GET. It makes no changes to `Tools.ps1`, collector behavior, Windows TLS configuration, or proxy configuration. Downloaded files are never executed.
## Run on the affected host
Use the same RMM account, PowerShell executable, and bootstrap as the failing job. Keep the existing Tools bootstrap unchanged and replace the final collector invocation with this block after publishing the diagnostic script to the repository:
```powershell
$DiagnosticClient = New-Object Net.WebClient
try {
$DiagnosticText = $DiagnosticClient.DownloadString(
'https://dev.emberkom.com/emberkom/management-scripts/raw/branch/master/Test-FileDownload.ps1'
)
}
finally { $DiagnosticClient.Dispose() }
& ([scriptblock]::Create($DiagnosticText)) `
-URL 'https://xfer.emberkom.com/shares/tools/folders/9b9988fb-c53a-4833-a4ad-68d01a3021bb/files/dsa-collect-windows-amd64-exe/download/dsa-collect-windows-amd64.exe' `
-OutputDirectory "$Env:ProgramData\Emberkom\Output" `
-TimeoutSeconds 120
```
Do not invoke it through `Download-File`, since that is the function being investigated. Alternatively, save the diagnostic on the affected host and call it directly with `& 'C:\path\Test-FileDownload.ps1' -URL '<download URL>'`. If Tools cannot load on a legacy engine, a standalone run still tests the other methods and records that the helper was unavailable.
Use `powershell.exe`, not ISE or an embedded PowerShell host. The diagnostic rejects other executables instead of guessing how to launch equivalent child processes.
The mandatory `-URL` can be any HTTP(S) file URL without embedded credentials. `-OutputDirectory` defaults to the account's temporary directory. Each invocation creates its own `file-download-diagnostic-<GUID>` subdirectory, containing `report.txt` and `report.clixml`. No reports are uploaded automatically. Retrieve `report.txt` from the affected host for analysis; the CLIXML file preserves structured details.
There are ten sequential probes: environment, HTTP headers/ranges, and two destination variants for each of four download paths. Eight probes download the payload, so use a small representative file. Each probe has a 120-second default wall-clock limit, followed by a separate cleanup attempt of at most 15 seconds for BITS-related probes. Allow about 22 minutes plus process startup/reporting overhead for the worst case, or specify a shorter timeout. Large downloads and hashing count toward the timeout.
## What the report captures
- OS, CLR and PowerShell versions, process bitness, executable, identity, BITS service/module information, and proxy/TLS settings. Missing registry values are recorded rather than invented as defaults.
- Definitions and SHA-256 hashes of the loaded helper functions, plus command resolution. The full Tools script is not executed by the diagnostic.
- HTTP redirect chains, status, content length, range support, and selected response headers. Cookies and authorization headers are not collected.
- Independent downloads to new destinations and existing empty destinations, byte counts, hashes, first bytes, timing, and full exceptions.
- BITS job state changes and byte counts before completion, and file snapshots immediately after transfer and one second later.
Child processes use the caller's PowerShell executable and account, with its .NET TLS selection reproduced in the child only. Existing pooled connections, custom in-memory proxy objects, and certificate callbacks are not copied. The helper probe copies the captured functions, redirects helper logging into diagnostic scratch, and adds ownership tags to BITS jobs. A shadowed/non-native `Start-BitsTransfer` is recorded and the helper probe is skipped rather than risking cleanup of unidentified jobs.
Scratch filenames retain the original URL's extension when usable, but the production destination is never touched. Path-specific security rules may therefore behave differently. The loaded helper's logging overrides are copied only into its child process.
Only jobs matching both the diagnostic's unique run ID and exact probe tag are removed. Scratch cleanup does not recurse or touch the production executable. Cleanup errors and unfinished probes remain visible in the report. Reports include local machine/account details and loaded source definitions; review them before sharing.
The code uses PowerShell 2 syntax and APIs available to legacy .NET, but local validation on PowerShell 5.1 does not prove execution on 2, 3, or 4. Run the diagnostic on actual legacy runtimes before declaring them supported. Full `Tools.ps1` loadability is a separate issue: it already contains newer syntax and commands, including `-in`/`-notin`, `::new()`, `ConvertFrom-Json`, and `Get-FileHash` outside the downloader.
## Interpret the comparison
| Observation | Next investigation |
|---|---|
| Only the helper fails | Compare `HelperBitsSource` with the original URL, the redirect chain, and URL-resolution requests. |
| BITS fails and direct GET succeeds | Compare service identity, WinHTTP/.NET proxy and TLS settings, HTTP behavior, and BITS error codes. |
| Both transports fail | Investigate endpoint responses, certificate trust, connectivity, and the affected machine's configuration. |
| BITS reports bytes transferred but the resulting file differs | Inspect completion errors, destination access, and post-download changes. |
| All paths produce the same nonempty hash | Failure was not reproduced. This run does not justify changing the shared transport. |
Neither a positive byte count nor matching hashes authenticates a publisher's executable. These are diagnostic comparisons, not a replacement for publisher-supplied integrity information.
## Shared caller audit
The audit found 24 active call sites across 20 files: 19 scripts plus five wrapper call sites in `Tools.ps1`. Commented-out calls and tests are excluded.
| Callers | Required behavior |
|---|---|
| `Run-Script`, `Source-PSScript`, `Uninstall-MicrosoftOffice` | Return one completed script path with its extension; allow immediate sourcing/execution. |
| `Install-MSI`, `Install-4KVideoDownloader`, `Install-LiquidFilesOutlookAgent`, `Install-SimpleInOut`, `Install-SpecsIntact`, `Install-Wireguard` | Return a completed installer path for immediate MSI invocation and later cleanup. |
| `Install-Nextcloud`, `Install-OpenVPN`, `Install-VLC` | Work inside a subexpression or via positional URL; return only the downloaded path. |
| `Install-AutodeskDesktopConnector`, `Install-Enscape`, `Install-ESETManagementAgent`, `Install-MicrosoftOffice365`, `Install-SketchUp` | Honor an explicit destination; handle redirects and potentially large installers; finish before installation. |
| `Fix-AutodeskProductLicensing`, `Test-InternetBandwidth`, `Get-LiquidFilesCLI` | Retain a usable ZIP filename and extension for extraction and derived working-directory names. |
| `Get-DSAManifest`, `Fix-UpdateLocalHosts`, `Remove-AllESETProducts`, `Install-LocalTools` | Honor fixed executable paths and complete replacement before execution. Caller-level caching remains separate. |
All concrete download URLs in this audit use HTTPS. The public wrappers also accept caller-provided HTTP(S) URLs. No audited caller consumes a BITS job object or explicitly controls background transfers/resume.
`Download-File` currently calls `Get-AbsoluteURI`, which first calls `IsURLValid`; both issue GET requests without disposing their responses. The final response URI is then passed to BITS. This is an observed implementation detail to investigate, not proof that BITS should be replaced.
Any subsequent shared fix must preserve URL binding, explicit/automatic filenames, synchronous completion, one success path on the output stream, redirects, large-file streaming, shell architectures, and `Run-Script` shared scope. It must also address validation and failure propagation before callers install or extract content. Production implementation and rollout follow the diagnostic findings; this change adds no fallback or collector-specific transport option.
## Local verification
Run `tests\Test-FileDownload.Tests.ps1` in both System32 and SysWOW64 Windows PowerShell. The suite serves synthetic payloads over loopback, tests both destination variants, redirects, chunked/empty/truncated/error responses, timeouts, hashing, and preservation of an unrelated BITS job. It requires access to the BITS service but performs no external downloads, installations, or uploads.
The affected RMM host and actual PowerShell 2-4 engines must still be tested separately. No local result substitutes for those environments.
Local verification on 2026-09-30 passed under both 32-bit and 64-bit PowerShell 5.1. A live 32-bit run against the AMD64 collector URL returned 3,455,488 bytes with SHA-256 `CC693EF2FEEEF05C99410B6F7A05AE2621EA0B89BA6D3E12C50B7F059D557F99` for every download method and destination variant. This is an observed comparison hash, not a publisher-provided trust anchor. The failure was not reproduced, so the current recommendation is to retain the shared transport until the affected RMM run supplies evidence.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More