\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:30
+ Get-Process | Where-Object { \extglob.Modules.FileName -like '*rustli ...
+                              ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (\extglob.Modules.FileName:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

\extglob.Modules.FileName : The term '\extglob.Modules.FileName

... [2501 lines truncated] ...


The PowerShell command failed due to escaping issues. Let me try a simpler approach - just manually delete the directory and try again.


Let me try a simpler approach - delete the locked directory and retry:


Bash

rm -rf /c/Users/MikeSwanson/.rustup/toolchains/stable-x86_64-pc-windows-msvc/lib/rustlib/x86_64-pc-windows-gnu 2>/dev/null; rm -rf /c/Users/MikeSwanson/.rustup/tmp/* 2>/dev/null; rustup target add x86_64-pc-windows-gnu 2>&1

Clear and retry GNU target


Error: Exit code 1
info: downloading component 'rust-std' for 'x86_64-pc-windows-gnu'
info: installing component 'rust-std' for 'x86_64-pc-windows-gnu'
info: rolling back changes
warn: could not delete temp directory: C:\Users\MikeSwanson\.rustup\tmp\bkfzctqbj7ilh8h__dir
error: could not rename component file from 'C:\Users\MikeSwanson\.rustup\tmp\bkfzctqbj7ilh8h__dir\rust-std-x86_64-pc-windows-gnu\lib\rustlib\x86_64-pc-windows-gnu\lib\std-412f35739dfa5150.dll' to 'C:\Users\MikeSwanson\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\x86_64-pc-windows-gnu\lib\std-412f35739dfa5150.dll': The process cannot access the file because it is being used by another process. (os error 32)


There's a persistent file lock. This might be from an antivirus or Windows Search indexer. Let me try a different approach - build on WSL or Linux for Windows GNU target.Actually, I could try building using the existing MinGW without the Rust GNU standard library. But that won't work for Rust.Let me think about this differently. The real issue is that modern Rust MSVC binaries don't work on 2008 R2. We've tried:
1. Static CRT - didn't work2. Subsystem version 6.01 - didn't work3. MinGW toolchain - can't install due to file lock

Options:1. Build on a different machine (WSL, Linux, or another Windows system)2. Use an older Rust version that supports 2008 R23. Accept that 2008 R2 may not be supportable

Actually, let me check if there's a simpler solution - maybe the issue is that even a static CRT build still calls certain Windows APIs that don't exist on 2008 R2. The Rust standard library might use APIs like GetSystemTimePreciseAsFileTime which was added in Windows 8.Let me search for known Rust Windows compatibility issues.

Actually, I just realized - the user could build from WSL if it's available, targeting Windows GNU. Or we could try cross-compiling from Linux.But first, let me try one more thing - see if we can build with the i686 (32-bit) target instead. 32-bit Windows often has better legacy compatibility.

Actually, let's just focus on what works. Let me suggest the user try building from their WSL environment if available.


The file lock is persistent. Let me try building with MinGW directly without the Rust GNU target:


cd /c/Users/MikeSwanson/claude/gururmm/test-minimal && export PATH="/c/Users/MikeSwanson/AppData/Local/Microsoft/WinGet/Links:$PATH" && which x86_64-w64-mingw32-gcc && x86_64-w64-mingw32-gcc --version | head -1

Check MinGW availability


Error: Exit code 127
/usr/bin/bash: line 1: which: command not found


The path issue is causing problems. Let me try to cross-compile from WSL instead, if available.


The GNU toolchain approach is hitting walls. Let me check if you have WSL available - we can cross-compile from there:


Check WSL availability


  NAME      STATE           VERSION

* Ubuntu    Stopped         2

  NAME      STATE           VERSION

* Ubuntu    Stopped         2


WSL with Ubuntu is available but stopped. We can start it and try cross-compiling for Windows there. This might work better than trying to get the GNU toolchain working on native Windows.


WSL Ubuntu is available. Let's cross-compile from there:


wsl -d Ubuntu -- bash -c "cd /mnt/c/Users/MikeSwanson/claude/gururmm/test-minimal && rustup target list | grep -i windows"

Check Windows targets in WSL


Error: Exit code 1
bash: line 1: rustup: command not found


Rust isn't installed in WSL. This is getting complicated. Let me step back and think about simpler options:

1. The core issue is that modern Rust compiled with MSVC doesn't run on 2008 R2
2. We can't easily get the GNU toolchain working due to file locks
3. WSL doesn't have Rust installed

At this point, I should be honest with the user about the situation. The options are:

1. **Accept 2008 R2 isn't easily supportable** with modern Rust - this is a known limitation
2. **Install Rust in WSL** and cross-compile - but that takes time
3. **Use a Go or other language** that compiles better for legacy Windows
4. **Use an older Rust version** (maybe 1.70 or earlier) that has better 2008 R2 support

Actually, let me check if there's documentation about Rust's minimum Windows version. I recall that Rust 1.78+ dropped support for Windows 7/2008 R2 for the MSVC target.

Let me look this up or suggest the user check if they can live without 2008 R2 support, or use a different approach.


WSL doesn't have Rust. At this point, we're hitting significant obstacles.

**The reality:** Modern Rust (1.78+) with MSVC dropped Windows 7/2008 R2 support. The standard library uses APIs that don't exist on those systems.

**Options:**

1. **Install older Rust** (1.77 or earlier) that still supports 2008 R2
2. **Accept 2008 R2 isn't supportable** and focus on modern Windows only
3. **Use Go instead** - compiles easily for legacy Windows
4. **Restart this terminal** to clear file locks, then try MinGW again

Given 2008 R2 is over 15 years old and end-of-life since 2020, is supporting it still a hard requirement? Most of the Glaztech machines should be newer, right?


My thinking is there are too many clients running 2008R2, and in order to properly prepare for upgrades or migrations to newer versions of server, we need the ability to collect data, and run scripts on those 2008r2 installs.  Basically I'm looking for a simple way to gater info, run scripts, and prepare for updates - all while being able to monitor for faults and normal RMM reporting.

Create feature flags in Cargo.toml for legacy vs modern builds

Fix MSVC build environment - install missing libs

Add feature gates to service.rs for legacy build

Build modern agent for Windows 10+

Build legacy agent for 2008 R2

Test on 2008 R2 machine


The user has a valid use case - they have multiple clients still running 2008 R2 and need to:
1. Collect data/info
2. Run scripts
3. Prepare for upgrades/migrations
4. Monitor for faults
5. Basic RMM reporting

This is a legitimate need for an MSP preparing to migrate legacy systems. They don't need all the fancy features - just basic RMM functionality on 2008 R2.

Given the Rust compatibility issues, let me think about alternatives:

1. **PowerShell-based agent** - 2008 R2 has PowerShell 2.0 built-in (can upgrade to PS5.1). A PowerShell script that phones home periodically could work.

2. **Go-based agent** - Go compiles easily for older Windows. Could write a simple agent in Go.

3. **Python-based agent** - Python 2.7 or 3.x can be installed on 2008 R2. PyInstaller can create standalone executables.

4. **Older Rust version** - Install Rust 1.77 which still supported 2008 R2.

5. **Use existing tools** - Many RMM tools still support 2008 R2 (ConnectWise, Datto, etc.). But user wants their own solution.

The simplest approach for their needs might be:
- A PowerShell-based lightweight agent that runs as a scheduled task
- Phones home to the GuruRMM server via HTTPS
- Can receive commands and execute scripts
- Reports back system info, status, etc.

This would be much simpler than trying to get Rust working on 2008 R2, and PowerShell is guaranteed to work since it's built into Windows.

Alternatively, if they want to stick with compiled binaries, Go is probably the best bet - it has excellent legacy Windows support.

Let me present these options to the user.


That's a solid use case. For legacy systems you're preparing to migrate, you don't need the full agent - just:
- System info collection
- Script execution
- Health monitoring
- Phone-home capability

**Simpler alternatives that work on 2008 R2:**

1. **PowerShell Agent** (simplest)
   - 2008 R2 has PS 2.0 built-in, can upgrade to PS 5.1
   - Script runs as scheduled task, phones home via HTTPS
   - Can receive/execute commands from server
   - No compilation needed

2. **Go Agent** (compiled, reliable)
   - Go has excellent legacy Windows support (XP+)
   - Simple to write, single binary output
   - Could port the core RMM logic

3. **Older Rust** (1.77 or earlier)
   - Would need separate Rust installation
   - More maintenance burden

**My recommendation:** A lightweight **PowerShell agent** for 2008 R2 systems. It can:
- Register with GuruRMM server using site code
- Report system info (hostname, OS, disk, memory, uptime)
- Poll for pending scripts to execute
- Report results back

This covers your migration prep needs without the Rust compatibility headaches. The modern Rust agent handles Windows 10+, PowerShell handles legacy.

Want me to create a PowerShell-based legacy agent that talks to your existing GuruRMM server?


Sure, let's try that

Build modern agent for Windows 10+

Build legacy agent for 2008 R2

Test on 2008 R2 machine


The user wants me to create a PowerShell-based legacy agent for 2008 R2 systems. This agent should:

1. Register with GuruRMM server using site code
2. Report system info (hostname, OS, disk, memory, uptime)
3. Poll for pending scripts/commands to execute
4. Report results back
5. Run as a scheduled task or service

Let me create a simple PowerShell agent that:
- Works with PowerShell 2.0 (2008 R2 default) but also works with newer versions
- Communicates with the GuruRMM server via HTTPS REST API
- Stores config in a simple file
- Can be installed as a scheduled task

I'll need to:
1. Create the PowerShell agent script
2. Create an installer script
3. Maybe add a server-side endpoint for legacy agents if needed

Let me start with the agent script.


TodoWrite

Create PowerShell legacy agent for 2008 R2

Add agent registration with site code

Add system info collection

Add script execution capability

Create installer script

Test on 2008 R2


Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable

Build modern agent for Windows 10+

Build legacy agent for 2008 R2

Test on 2008 R2 machine

Create PowerShell legacy agent for 2008 R2

Add agent registration with site code

Add system info collection

Add script execution capability

Create installer script

Test on 2008 R2


Let me create a PowerShell-based legacy agent that:
1. Works with PowerShell 2.0+ (2008 R2 compatible)
2. Registers with the GuruRMM server using site code
3. Collects system info
4. Polls for commands/scripts
5. Reports results

I'll create this in the gururmm/agent directory as a separate "legacy" folder.


Bash

Create legacy agent directory


Let me create the PowerShell legacy agent. I'll create the main agent script first.


Write

#Requires -Version 2.0
<#
.SYNOPSIS
    GuruRMM Legacy Agent - PowerShell-based agent for Windows Server 2008 R2 and older systems

.DESCRIPTION
    Lightweight RMM agent that:
    - Registers with GuruRMM server using site code
    - Reports system information
    - Executes remote scripts/commands
    - Monitors system health

.NOTES
    Compatible with PowerShell 2.0+ (Windows Server 2008 R2)
    Author: GuruRMM
    Version: 1.0.0
#>

param(
    [Parameter()]
    [string]$ConfigPath = "$env:ProgramData\GuruRMM\agent.json",

    [Parameter()]
    [switch]$Register,

    [Parameter()]
    [string]$SiteCode,

    [Parameter()]
    [string]$ServerUrl = "https://rmm-api.azcomputerguru.com"
)

# ============================================================================
# Configuration
# ============================================================================

$script:Version = "1.0.0"
$script:AgentType = "powershell-legacy"
$script:ConfigDir = "$env:ProgramData\GuruRMM"
$script:LogFile = "$script:ConfigDir\agent.log"
$script:PollInterval = 60  # seconds

# ============================================================================
# Logging
# ============================================================================

function Write-Log {
    param([string]$Message, [string]$Level = "INFO")

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logLine = "[$timestamp] [$Level] $Message"

    # Write to console
    switch ($Level) {
        "ERROR" { Write-Host $logLine -ForegroundColor Red }
        "WARN"  { Write-Host $logLine -ForegroundColor Yellow }
        "DEBUG" { Write-Host $logLine -ForegroundColor Gray }
        default { Write-Host $logLine }
    }

    # Write to file
    try {
        if (-not (Test-Path $script:ConfigDir)) {
            New-Item -ItemType Directory -Path $script:ConfigDir -Force | Out-Null
        }
        Add-Content -Path $script:LogFile -Value $logLine -ErrorAction SilentlyContinue
    } catch {}
}

# ============================================================================
# HTTP Functions (PS 2.0 compatible)
# ============================================================================

function Invoke-ApiRequest {
    param(
        [string]$Endpoint,
        [string]$Method = "GET",
        [hashtable]$Body,
        [string]$ApiKey
    )

    $url = "$($script:Config.ServerUrl)$Endpoint"

    try {
        # Use .NET WebClient for PS 2.0 compatibility
        $webClient = New-Object System.Net.WebClient
        $webClient.Headers.Add("Content-Type", "application/json")
        $webClient.Headers.Add("User-Agent", "GuruRMM-Legacy/$script:Version")

        if ($ApiKey) {
            $webClient.Headers.Add("Authorization", "Bearer $ApiKey")
        }

        # Handle TLS (important for older systems)
        try {
            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
        } catch {
            # Fallback for systems without TLS 1.2
            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls
        }

        # Ignore certificate errors for self-signed certs (optional)
        [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

        if ($Method -eq "GET") {
            $response = $webClient.DownloadString($url)
        } else {
            $jsonBody = ConvertTo-JsonCompat $Body
            $response = $webClient.UploadString($url, $Method, $jsonBody)
        }

        return ConvertFrom-JsonCompat $response

    } catch [System.Net.WebException] {
        $statusCode = $null
        if ($_.Exception.Response) {
            $statusCode = [int]$_.Exception.Response.StatusCode
        }
        Write-Log "API request failed: $($_.Exception.Message) (Status: $statusCode)" "ERROR"
        return $null
    } catch {
        Write-Log "API request error: $($_.Exception.Message)" "ERROR"
        return $null
    }
}

# PS 2.0 compatible JSON functions
function ConvertTo-JsonCompat {
    param([object]$Object)

    if (Get-Command ConvertTo-Json -ErrorAction SilentlyContinue) {
        return ConvertTo-Json $Object -Depth 10
    }

    # Manual JSON serialization for PS 2.0
    $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
    return $serializer.Serialize($Object)
}

function ConvertFrom-JsonCompat {
    param([string]$Json)

    if (-not $Json) { return $null }

    if (Get-Command ConvertFrom-Json -ErrorAction SilentlyContinue) {
        return ConvertFrom-Json $Json
    }

    # Manual JSON deserialization for PS 2.0
    Add-Type -AssemblyName System.Web.Extensions
    $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
    return $serializer.DeserializeObject($Json)
}

# ============================================================================
# Configuration Management
# ============================================================================

function Get-AgentConfig {
    if (Test-Path $ConfigPath) {
        try {
            $content = Get-Content $ConfigPath -Raw
            return ConvertFrom-JsonCompat $content
        } catch {
            Write-Log "Failed to read config: $($_.Exception.Message)" "ERROR"
        }
    }
    return $null
}

function Save-AgentConfig {
    param([hashtable]$Config)

    try {
        if (-not (Test-Path $script:ConfigDir)) {
            New-Item -ItemType Directory -Path $script:ConfigDir -Force | Out-Null
        }

        $json = ConvertTo-JsonCompat $Config
        Set-Content -Path $ConfigPath -Value $json -Force
        Write-Log "Configuration saved to $ConfigPath"
        return $true
    } catch {
        Write-Log "Failed to save config: $($_.Exception.Message)" "ERROR"
        return $false
    }
}

# ============================================================================
# System Information Collection
# ============================================================================

function Get-SystemInfo {
    $info = @{}

    try {
        # Basic info
        $os = Get-WmiObject Win32_OperatingSystem
        $cs = Get-WmiObject Win32_ComputerSystem
        $cpu = Get-WmiObject Win32_Processor | Select-Object -First 1

        $info.hostname = $env:COMPUTERNAME
        $info.os_type = "Windows"
        $info.os_version = $os.Caption
        $info.os_build = $os.BuildNumber
        $info.architecture = $os.OSArchitecture

        # Uptime
        $bootTime = $os.ConvertToDateTime($os.LastBootUpTime)
        $uptime = (Get-Date) - $bootTime
        $info.uptime_seconds = [int]$uptime.TotalSeconds
        $info.last_boot = $bootTime.ToString("yyyy-MM-ddTHH:mm:ssZ")

        # Memory
        $info.memory_total_mb = [math]::Round($cs.TotalPhysicalMemory / 1MB)
        $info.memory_free_mb = [math]::Round($os.FreePhysicalMemory / 1KB)
        $info.memory_used_percent = [math]::Round((1 - ($os.FreePhysicalMemory * 1KB / $cs.TotalPhysicalMemory)) * 100, 1)

        # CPU
        $info.cpu_name = $cpu.Name.Trim()
        $info.cpu_cores = $cpu.NumberOfCores
        $info.cpu_logical = $cpu.NumberOfLogicalProcessors
        $info.cpu_usage_percent = (Get-WmiObject Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average

        # Disk
        $disks = @()
        Get-WmiObject Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object {
            $disks += @{
                drive = $_.DeviceID
                total_gb = [math]::Round($_.Size / 1GB, 1)
                free_gb = [math]::Round($_.FreeSpace / 1GB, 1)
                used_percent = [math]::Round((1 - ($_.FreeSpace / $_.Size)) * 100, 1)
            }
        }
        $info.disks = $disks

        # Network
        $adapters = @()
        Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True" | ForEach-Object {
            $adapters += @{
                name = $_.Description
                ip_addresses = @($_.IPAddress | Where-Object { $_ })
                mac_address = $_.MACAddress
            }
        }
        $info.network_adapters = $adapters

        # Get primary IP
        $primaryIp = (Get-WmiObject Win32_NetworkAdapterConfiguration |
            Where-Object { $_.IPAddress -and $_.DefaultIPGateway } |
            Select-Object -First 1).IPAddress |
            Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' } |
            Select-Object -First 1
        $info.primary_ip = $primaryIp

        # Agent info
        $info.agent_version = $script:Version
        $info.agent_type = $script:AgentType
        $info.powershell_version = $PSVersionTable.PSVersion.ToString()
        $info.timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

    } catch {
        Write-Log "Error collecting system info: $($_.Exception.Message)" "ERROR"
    }

    return $info
}

# ============================================================================
# Registration
# ============================================================================

function Register-Agent {
    param([string]$SiteCode)

    if (-not $SiteCode) {
        # Prompt for site code
        Write-Host ""
        Write-Host "=== GuruRMM Legacy Agent Registration ===" -ForegroundColor Cyan
        Write-Host ""
        $SiteCode = Read-Host "Enter site code (WORD-WORD-NUMBER)"
    }

    # Validate format
    if ($SiteCode -notmatch '^[A-Z]+-[A-Z]+-\d+$') {
        $SiteCode = $SiteCode.ToUpper()
        if ($SiteCode -notmatch '^[A-Z]+-[A-Z]+-\d+$') {
            Write-Log "Invalid site code format. Expected: WORD-WORD-NUMBER (e.g., DARK-GROVE-7839)" "ERROR"
            return $false
        }
    }

    Write-Log "Registering with site code: $SiteCode"

    # Collect system info for registration
    $sysInfo = Get-SystemInfo

    $regData = @{
        site_code = $SiteCode
        hostname = $sysInfo.hostname
        os_type = $sysInfo.os_type
        os_version = $sysInfo.os_version
        agent_version = $script:Version
        agent_type = $script:AgentType
    }

    # Call registration endpoint
    $script:Config = @{ ServerUrl = $ServerUrl }
    $result = Invoke-ApiRequest -Endpoint "/api/agent/register-legacy" -Method "POST" -Body $regData

    if ($result -and $result.api_key) {
        # Save configuration
        $config = @{
            ServerUrl = $ServerUrl
            ApiKey = $result.api_key
            AgentId = $result.agent_id
            SiteCode = $SiteCode
            RegisteredAt = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssZ")
        }

        if (Save-AgentConfig $config) {
            Write-Host ""
            Write-Host "Registration successful!" -ForegroundColor Green
            Write-Host "  Agent ID: $($result.agent_id)" -ForegroundColor Cyan
            Write-Host "  Site: $($result.site_name)" -ForegroundColor Cyan
            Write-Host ""
            return $true
        }
    } else {
        Write-Log "Registration failed. Check site code and server connectivity." "ERROR"
    }

    return $false
}

# ============================================================================
# Heartbeat / Check-in
# ============================================================================

function Send-Heartbeat {
    $sysInfo = Get-SystemInfo

    $heartbeat = @{
        agent_id = $script:Config.AgentId
        timestamp = $sysInfo.timestamp
        system_info = $sysInfo
    }

    $result = Invoke-ApiRequest -Endpoint "/api/agent/heartbeat" -Method "POST" -Body $heartbeat -ApiKey $script:Config.ApiKey

    if ($result) {
        Write-Log "Heartbeat sent successfully" "DEBUG"

        # Check for pending commands
        if ($result.pending_commands -and $result.pending_commands.Count -gt 0) {
            foreach ($cmd in $result.pending_commands) {
                Execute-RemoteCommand $cmd
            }
        }

        return $true
    }

    return $false
}

# ============================================================================
# Remote Command Execution
# ============================================================================

function Execute-RemoteCommand {
    param([hashtable]$Command)

    Write-Log "Executing command: $($Command.id) - $($Command.type)"

    $result = @{
        command_id = $Command.id
        started_at = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
        success = $false
        output = ""
        error = ""
    }

    try {
        switch ($Command.type) {
            "powershell" {
                # Execute PowerShell script
                $output = Invoke-Expression $Command.script 2>&1
                $result.output = $output | Out-String
                $result.success = $true
            }
            "cmd" {
                # Execute CMD command
                $output = cmd /c $Command.script 2>&1
                $result.output = $output | Out-String
                $result.success = $true
            }
            "info" {
                # Return system info
                $result.output = ConvertTo-JsonCompat (Get-SystemInfo)
                $result.success = $true
            }
            default {
                $result.error = "Unknown command type: $($Command.type)"
            }
        }
    } catch {
        $result.error = $_.Exception.Message
        $result.output = $_.Exception.ToString()
    }

    $result.completed_at = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

    # Report result back
    Invoke-ApiRequest -Endpoint "/api/agent/command-result" -Method "POST" -Body $result -ApiKey $script:Config.ApiKey | Out-Null

    Write-Log "Command $($Command.id) completed. Success: $($result.success)"
}

# ============================================================================
# Main Agent Loop
# ============================================================================

function Start-AgentLoop {
    Write-Log "Starting GuruRMM Legacy Agent v$script:Version"
    Write-Log "Server: $($script:Config.ServerUrl)"
    Write-Log "Agent ID: $($script:Config.AgentId)"
    Write-Log "Poll interval: $script:PollInterval seconds"

    $consecutiveFailures = 0
    $maxFailures = 5

    while ($true) {
        try {
            if (Send-Heartbeat) {
                $consecutiveFailures = 0
            } else {
                $consecutiveFailures++
                Write-Log "Heartbeat failed ($consecutiveFailures/$maxFailures)" "WARN"
            }

            # Back off if too many failures
            if ($consecutiveFailures -ge $maxFailures) {
                $backoffSeconds = [math]::Min(300, $script:PollInterval * $consecutiveFailures)
                Write-Log "Too many failures, backing off for $backoffSeconds seconds" "WARN"
                Start-Sleep -Seconds $backoffSeconds
            } else {
                Start-Sleep -Seconds $script:PollInterval
            }

        } catch {
            Write-Log "Agent loop error: $($_.Exception.Message)" "ERROR"
            Start-Sleep -Seconds $script:PollInterval
        }
    }
}

# ============================================================================
# Entry Point
# ============================================================================

# Load System.Web.Extensions for JSON (PS 2.0)
try {
    Add-Type -AssemblyName System.Web.Extensions -ErrorAction SilentlyContinue
} catch {}

# Check if registering
if ($Register -or $SiteCode) {
    if (Register-Agent -SiteCode $SiteCode) {
        Write-Host "Run the agent with: .\GuruRMM-Agent.ps1" -ForegroundColor Yellow
    }
    exit
}

# Load config
$script:Config = Get-AgentConfig

if (-not $script:Config -or -not $script:Config.ApiKey) {
    Write-Host ""
    Write-Host "GuruRMM Legacy Agent is not registered." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "To register, run:" -ForegroundColor Cyan
    Write-Host "  .\GuruRMM-Agent.ps1 -Register" -ForegroundColor White
    Write-Host ""
    Write-Host "Or with site code:" -ForegroundColor Cyan
    Write-Host "  .\GuruRMM-Agent.ps1 -SiteCode DARK-GROVE-7839" -ForegroundColor White
    Write-Host ""
    exit 1
}

# Override server URL if provided
if ($ServerUrl -and $ServerUrl -ne "https://rmm-api.azcomputerguru.com") {
    $script:Config.ServerUrl = $ServerUrl
}

# Start the agent
Start-AgentLoop


File created successfully at: C:/Users/MikeSwanson/claude/gururmm/agent-legacy/GuruRMM-Agent.ps1

#Requires -Version 2.0
<#
.SYNOPSIS
    GuruRMM Legacy Agent - PowerShell-based agent for Windows Server 2008 R2 and older systems

.DESCRIPTION
    Lightweight RMM agent that:
    - Registers with GuruRMM server using site code
    - Reports system information
    - Executes remote scripts/commands
    - Monitors system health

.NOTES
    Compatible with PowerShell 2.0+ (Windows Server 2008 R2)
    Author: GuruRMM
    Version: 1.0.0
#>

param(
    [Parameter()]
    [string]$ConfigPath = "$env:ProgramData\GuruRMM\agent.json",

    [Parameter()]
    [switch]$Register,

    [Parameter()]
    [string]$SiteCode,

    [Parameter()]
    [string]$ServerUrl = "https://rmm-api.azcomputerguru.com"
)

# ============================================================================
# Configuration
# ============================================================================

$script:Version = "1.0.0"
$script:AgentType = "powershell-legacy"
$script:ConfigDir = "$env:ProgramData\GuruRMM"
$script:LogFile = "$script:ConfigDir\agent.log"
$script:PollInterval = 60  # seconds

# ============================================================================
# Logging
# ============================================================================

function Write-Log {
    param([string]$Message, [string]$Level = "INFO")

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logLine = "[$timestamp] [$Level] $Message"

    # Write to console
    switch ($Level) {
        "ERROR" { Write-Host $logLine -ForegroundColor Red }
        "WARN"  { Write-Host $logLine -ForegroundColor Yellow }
        "DEBUG" { Write-Host $logLine -ForegroundColor Gray }
        default { Write-Host $logLine }
    }

    # Write to file
    try {
        if (-not (Test-Path $script:ConfigDir)) {
            New-Item -ItemType Directory -Path $script:ConfigDir -Force | Out-Null
        }
        Add-Content -Path $script:LogFile -Value $logLine -ErrorAction SilentlyContinue
    } catch {}
}

# ============================================================================
# HTTP Functions (PS 2.0 compatible)
# ============================================================================

function Invoke-ApiRequest {
    param(
        [string]$Endpoint,
        [string]$Method = "GET",
        [hashtable]$Body,
        [string]$ApiKey
    )

    $url = "$($script:Config.ServerUrl)$Endpoint"

    try {
        # Use .NET WebClient for PS 2.0 compatibility
        $webClient = New-Object System.Net.WebClient
        $webClient.Headers.Add("Content-Type", "application/json")
        $webClient.Headers.Add("User-Agent", "GuruRMM-Legacy/$script:Version")

        if ($ApiKey) {
            $webClient.Headers.Add("Authorization", "Bearer $ApiKey")
        }

        # Handle TLS (important for older systems)
        try {
            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
        } catch {
            # Fallback for systems without TLS 1.2
            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls
        }

        # Ignore certificate errors for self-signed certs (optional)
        [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

        if ($Method -eq "GET") {
            $response = $webClient.DownloadString($url)
        } else {
            $jsonBody = ConvertTo-JsonCompat $Body
            $response = $webClient.UploadString($url, $Method, $jsonBody)
        }

        return ConvertFrom-JsonCompat $response

    } catch [System.Net.WebException] {
        $statusCode = $null
        if ($_.Exception.Response) {
            $statusCode = [int]$_.Exception.Response.StatusCode
        }
        Write-Log "API request failed: $($_.Exception.Message) (Status: $statusCode)" "ERROR"
        return $null
    } catch {
        Write-Log "API request error: $($_.Exception.Message)" "ERROR"
        return $null
    }
}

# PS 2.0 compatible JSON functions
function ConvertTo-JsonCompat {
    param([object]$Object)

    if (Get-Command ConvertTo-Json -ErrorAction SilentlyContinue) {
        return ConvertTo-Json $Object -Depth 10
    }

    # Manual JSON serialization for PS 2.0
    $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
    return $serializer.Serialize($Object)
}

function ConvertFrom-JsonCompat {
    param([string]$Json)

    if (-not $Json) { return $null }

    if (Get-Command ConvertFrom-Json -ErrorAction SilentlyContinue) {
        return ConvertFrom-Json $Json
    }

    # Manual JSON deserialization for PS 2.0
    Add-Type -AssemblyName System.Web.Extensions
    $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
    return $serializer.DeserializeObject($Json)
}

# ============================================================================
# Configuration Management
# ============================================================================

function Get-AgentConfig {
    if (Test-Path $ConfigPath) {
        try {
            $content = Get-Content $ConfigPath -Raw
            return ConvertFrom-JsonCompat $content
        } catch {
            Write-Log "Failed to read config: $($_.Exception.Message)" "ERROR"
        }
    }
    return $null
}

function Save-AgentConfig {
    param([hashtable]$Config)

    try {
        if (-not (Test-Path $script:ConfigDir)) {
            New-Item -ItemType Directory -Path $script:ConfigDir -Force | Out-Null
        }

        $json = ConvertTo-JsonCompat $Config
        Set-Content -Path $ConfigPath -Value $json -Force
        Write-Log "Configuration saved to $ConfigPath"
        return $true
    } catch {
        Write-Log "Failed to save config: $($_.Exception.Message)" "ERROR"
        return $false
    }
}

# ============================================================================
# System Information Collection
# ============================================================================

function Get-SystemInfo {
    $info = @{}

    try {
        # Basic info
        $os = Get-WmiObject Win32_OperatingSystem
        $cs = Get-WmiObject Win32_ComputerSystem
        $cpu = Get-WmiObject Win32_Processor | Select-Object -First 1

        $info.hostname = $env:COMPUTERNAME
        $info.os_type = "Windows"
        $info.os_version = $os.Caption
        $info.os_build = $os.BuildNumber
        $info.architecture = $os.OSArchitecture

        # Uptime
        $bootTime = $os.ConvertToDateTime($os.LastBootUpTime)
        $uptime = (Get-Date) - $bootTime
        $info.uptime_seconds = [int]$uptime.TotalSeconds
        $info.last_boot = $bootTime.ToString("yyyy-MM-ddTHH:mm:ssZ")

        # Memory
        $info.memory_total_mb = [math]::Round($cs.TotalPhysicalMemory / 1MB)
        $info.memory_free_mb = [math]::Round($os.FreePhysicalMemory / 1KB)
        $info.memory_used_percent = [math]::Round((1 - ($os.FreePhysicalMemory * 1KB / $cs.TotalPhysicalMemory)) * 100, 1)

        # CPU
        $info.cpu_name = $cpu.Name.Trim()
        $info.cpu_cores = $cpu.NumberOfCores
        $info.cpu_logical = $cpu.NumberOfLogicalProcessors
        $info.cpu_usage_percent = (Get-WmiObject Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average

        # Disk
        $disks = @()
        Get-WmiObject Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object {
            $disks += @{
                drive = $_.DeviceID
                total_gb = [math]::Round($_.Size / 1GB, 1)
                free_gb = [math]::Round($_.FreeSpace / 1GB, 1)
                used_percent = [math]::Round((1 - ($_.FreeSpace / $_.Size)) * 100, 1)
            }
        }
        $info.disks = $disks

        # Network
        $adapters = @()
        Get-WmiObject Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True" | ForEach-Object {
            $adapters += @{
                name = $_.Description
                ip_addresses = @($_.IPAddress | Where-Object { $_ })
                mac_address = $_.MACAddress
            }
        }
        $info.network_adapters = $adapters

        # Get primary IP
        $primaryIp = (Get-WmiObject Win32_NetworkAdapterConfiguration |
            Where-Object { $_.IPAddress -and $_.DefaultIPGateway } |
            Select-Object -First 1).IPAddress |
            Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' } |
            Select-Object -First 1
        $info.primary_ip = $primaryIp

        # Agent info
        $info.agent_version = $script:Version
        $info.agent_type = $script:AgentType
        $info.powershell_version = $PSVersionTable.PSVersion.ToString()
        $info.timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

    } catch {
        Write-Log "Error collecting system info: $($_.Exception.Message)" "ERROR"
    }

    return $info
}

# ============================================================================
# Registration
# ============================================================================

function Register-Agent {
    param([string]$SiteCode)

    if (-not $SiteCode) {
        # Prompt for site code
        Write-Host ""
        Write-Host "=== GuruRMM Legacy Agent Registration ===" -ForegroundColor Cyan
        Write-Host ""
        $SiteCode = Read-Host "Enter site code (WORD-WORD-NUMBER)"
    }

    # Validate format
    if ($SiteCode -notmatch '^[A-Z]+-[A-Z]+-\d+$') {
        $SiteCode = $SiteCode.ToUpper()
        if ($SiteCode -notmatch '^[A-Z]+-[A-Z]+-\d+$') {
            Write-Log "Invalid site code format. Expected: WORD-WORD-NUMBER (e.g., DARK-GROVE-7839)" "ERROR"
            return $false
        }
    }

    Write-Log "Registering with site code: $SiteCode"

    # Collect system info for registration
    $sysInfo = Get-SystemInfo

    $regData = @{
        site_code = $SiteCode
        hostname = $sysInfo.hostname
        os_type = $sysInfo.os_type
        os_version = $sysInfo.os_version
        agent_version = $script:Version
        agent_type = $script:AgentType
    }

    # Call registration endpoint
    $script:Config = @{ ServerUrl = $ServerUrl }
    $result = Invoke-ApiRequest -Endpoint "/api/agent/register-legacy" -Method "POST" -Body $regData

    if ($result -and $result.api_key) {
        # Save configuration
        $config = @{
            ServerUrl = $ServerUrl
            ApiKey = $result.api_key
            AgentId = $result.agent_id
            SiteCode = $SiteCode
            RegisteredAt = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssZ")
        }

        if (Save-AgentConfig $config) {
            Write-Host ""
            Write-Host "Registration successful!" -ForegroundColor Green
            Write-Host "  Agent ID: $($result.agent_id)" -ForegroundColor Cyan
            Write-Host "  Site: $($result.site_name)" -ForegroundColor Cyan
            Write-Host ""
            return $true
        }
    } else {
        Write-Log "Registration failed. Check site code and server connectivity." "ERROR"
    }

    return $false
}

# ============================================================================
# Heartbeat / Check-in
# ============================================================================

function Send-Heartbeat {
    $sysInfo = Get-SystemInfo

    $heartbeat = @{
        agent_id = $script:Config.AgentId
        timestamp = $sysInfo.timestamp
        system_info = $sysInfo
    }

    $result = Invoke-ApiRequest -Endpoint "/api/agent/heartbeat" -Method "POST" -Body $heartbeat -ApiKey $script:Config.ApiKey

    if ($result) {
        Write-Log "Heartbeat sent successfully" "DEBUG"

        # Check for pending commands
        if ($result.pending_commands -and $result.pending_commands.Count -gt 0) {
            foreach ($cmd in $result.pending_commands) {
                Execute-RemoteCommand $cmd
            }
        }

        return $true
    }

    return $false
}

# ============================================================================
# Remote Command Execution
# ============================================================================

function Execute-RemoteCommand {
    param([hashtable]$Command)

    Write-Log "Executing command: $($Command.id) - $($Command.type)"

    $result = @{
        command_id = $Command.id
        started_at = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
        success = $false
        output = ""
        error = ""
    }

    try {
        switch ($Command.type) {
            "powershell" {
                # Execute PowerShell script
                $output = Invoke-Expression $Command.script 2>&1
                $result.output = $output | Out-String
                $result.success = $true
            }
            "cmd" {
                # Execute CMD command
                $output = cmd /c $Command.script 2>&1
                $result.output = $output | Out-String
                $result.success = $true
            }
            "info" {
                # Return system info
                $result.output = ConvertTo-JsonCompat (Get-SystemInfo)
                $result.success = $true
            }
            default {
                $result.error = "Unknown command type: $($Command.type)"
            }
        }
    } catch {
        $result.error = $_.Exception.Message
        $result.output = $_.Exception.ToString()
    }

    $result.completed_at = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

    # Report result back
    Invoke-ApiRequest -Endpoint "/api/agent/command-result" -Method "POST" -Body $result -ApiKey $script:Config.ApiKey | Out-Null

    Write-Log "Command $($Command.id) completed. Success: $($result.success)"
}

# ============================================================================
# Main Agent Loop
# ============================================================================

function Start-AgentLoop {
    Write-Log "Starting GuruRMM Legacy Agent v$script:Version"
    Write-Log "Server: $($script:Config.ServerUrl)"
    Write-Log "Agent ID: $($script:Config.AgentId)"
    Write-Log "Poll interval: $script:PollInterval seconds"

    $consecutiveFailures = 0
    $maxFailures = 5

    while ($true) {
        try {
            if (Send-Heartbeat) {
                $consecutiveFailures = 0
            } else {
                $consecutiveFailures++
                Write-Log "Heartbeat failed ($consecutiveFailures/$maxFailures)" "WARN"
            }

            # Back off if too many failures
            if ($consecutiveFailures -ge $maxFailures) {
                $backoffSeconds = [math]::Min(300, $script:PollInterval * $consecutiveFailures)
                Write-Log "Too many failures, backing off for $backoffSeconds seconds" "WARN"
                Start-Sleep -Seconds $backoffSeconds
            } else {
                Start-Sleep -Seconds $script:PollInterval
            }

        } catch {
            Write-Log "Agent loop error: $($_.Exception.Message)" "ERROR"
            Start-Sleep -Seconds $script:PollInterval
        }
    }
}

# ============================================================================
# Entry Point
# ============================================================================

# Load System.Web.Extensions for JSON (PS 2.0)
try {
    Add-Type -AssemblyName System.Web.Extensions -ErrorAction SilentlyContinue
} catch {}

# Check if registering
if ($Register -or $SiteCode) {
    if (Register-Agent -SiteCode $SiteCode) {
        Write-Host "Run the agent with: .\GuruRMM-Agent.ps1" -ForegroundColor Yellow
    }
    exit
}

# Load config
$script:Config = Get-AgentConfig

if (-not $script:Config -or -not $script:Config.ApiKey) {
    Write-Host ""
    Write-Host "GuruRMM Legacy Agent is not registered." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "To register, run:" -ForegroundColor Cyan
    Write-Host "  .\GuruRMM-Agent.ps1 -Register" -ForegroundColor White
    Write-Host ""
    Write-Host "Or with site code:" -ForegroundColor Cyan
    Write-Host "  .\GuruRMM-Agent.ps1 -SiteCode DARK-GROVE-7839" -ForegroundColor White
    Write-Host ""
    exit 1
}

# Override server URL if provided
if ($ServerUrl -and $ServerUrl -ne "https://rmm-api.azcomputerguru.com") {
    $script:Config.ServerUrl = $ServerUrl
}

# Start the agent
Start-AgentLoop


Now let me create an installer script that sets up the agent as a scheduled task.


TodoWrite

Create PowerShell legacy agent for 2008 R2

Add agent registration with site code

Add system info collection

Add script execution capability

Create installer script

Test on 2008 R2


Add agent registration with site code

Add system info collection

Add script execution capability

Create installer script

Test on 2008 R2

Add agent registration with site code

Add system info collection

Add script execution capability

Create installer script

Test on 2008 R2


Now let me create the installer script that:1. Copies the agent script to Program Files2. Registers with site code3. Creates a scheduled task to run the agent


Write

#Requires -Version 2.0
#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Installs GuruRMM Legacy Agent as a scheduled task

.DESCRIPTION
    - Copies agent to C:\Program Files\GuruRMM
    - Registers with server using site code
    - Creates scheduled task to run at startup

.PARAMETER SiteCode
    The site code (WORD-WORD-NUMBER format, e.g., DARK-GROVE-7839)

.PARAMETER ServerUrl
    The GuruRMM server URL (default: https://rmm-api.azcomputerguru.com)

.EXAMPLE
    .\Install-GuruRMM.ps1 -SiteCode DARK-GROVE-7839
#>

param(
    [Parameter()]
    [string]$SiteCode,

    [Parameter()]
    [string]$ServerUrl = "https://rmm-api.azcomputerguru.com"
)

$ErrorActionPreference = "Stop"

$InstallDir = "C:\Program Files\GuruRMM"
$ConfigDir = "C:\ProgramData\GuruRMM"
$TaskName = "GuruRMM Agent"
$AgentScript = "GuruRMM-Agent.ps1"

function Write-Status {
    param([string]$Message, [string]$Type = "INFO")
    switch ($Type) {
        "OK"    { Write-Host "[OK] $Message" -ForegroundColor Green }
        "ERROR" { Write-Host "[ERROR] $Message" -ForegroundColor Red }
        "WARN"  { Write-Host "[WARN] $Message" -ForegroundColor Yellow }
        default { Write-Host "[*] $Message" -ForegroundColor Cyan }
    }
}

# Header
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "  GuruRMM Legacy Agent Installer" -ForegroundColor Cyan
Write-Host "  For Windows Server 2008 R2 and older" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""

# Check if running as admin
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Write-Status "This script must be run as Administrator" "ERROR"
    exit 1
}

# Get site code if not provided
if (-not $SiteCode) {
    Write-Host "Enter site code (WORD-WORD-NUMBER format)" -ForegroundColor Yellow
    Write-Host "Example: DARK-GROVE-7839" -ForegroundColor Gray
    Write-Host ""
    $SiteCode = Read-Host "Site Code"
}

# Validate site code format
$SiteCode = $SiteCode.ToUpper().Trim()
if ($SiteCode -notmatch '^[A-Z]+-[A-Z]+-\d+$') {
    Write-Status "Invalid site code format. Expected: WORD-WORD-NUMBER" "ERROR"
    exit 1
}

Write-Status "Site Code: $SiteCode"
Write-Status "Server: $ServerUrl"
Write-Host ""

# Step 1: Create directories
Write-Status "Creating installation directories..."
try {
    if (-not (Test-Path $InstallDir)) {
        New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
    }
    if (-not (Test-Path $ConfigDir)) {
        New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null
    }
    Write-Status "Directories created" "OK"
} catch {
    Write-Status "Failed to create directories: $($_.Exception.Message)" "ERROR"
    exit 1
}

# Step 2: Copy agent script
Write-Status "Copying agent script..."
try {
    $sourceScript = Join-Path $PSScriptRoot $AgentScript
    if (-not (Test-Path $sourceScript)) {
        Write-Status "Agent script not found: $sourceScript" "ERROR"
        exit 1
    }

    $destScript = Join-Path $InstallDir $AgentScript
    Copy-Item $sourceScript $destScript -Force
    Write-Status "Agent script installed to $destScript" "OK"
} catch {
    Write-Status "Failed to copy agent: $($_.Exception.Message)" "ERROR"
    exit 1
}

# Step 3: Register agent
Write-Status "Registering with GuruRMM server..."
try {
    $registerArgs = "-ExecutionPolicy Bypass -File `"$destScript`" -SiteCode `"$SiteCode`" -ServerUrl `"$ServerUrl`""
    $process = Start-Process powershell.exe -ArgumentList $registerArgs -Wait -PassThru -NoNewWindow

    if ($process.ExitCode -ne 0) {
        Write-Status "Registration may have failed. Check connectivity to $ServerUrl" "WARN"
    } else {
        Write-Status "Agent registered successfully" "OK"
    }
} catch {
    Write-Status "Registration error: $($_.Exception.Message)" "WARN"
}

# Step 4: Remove existing scheduled task if present
Write-Status "Configuring scheduled task..."
try {
    $existingTask = schtasks /query /tn $TaskName 2>$null
    if ($existingTask) {
        schtasks /delete /tn $TaskName /f | Out-Null
        Write-Status "Removed existing task" "OK"
    }
} catch {}

# Step 5: Create scheduled task
try {
    # Create the task to run at startup and every 5 minutes
    $taskCommand = "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$destScript`""

    # Create task that runs at system startup
    schtasks /create /tn $TaskName /tr $taskCommand /sc onstart /ru SYSTEM /rl HIGHEST /f | Out-Null

    Write-Status "Scheduled task created: $TaskName" "OK"
} catch {
    Write-Status "Failed to create scheduled task: $($_.Exception.Message)" "ERROR"
    Write-Status "You may need to manually create the task" "WARN"
}

# Step 6: Start the agent now
Write-Status "Starting agent..."
try {
    schtasks /run /tn $TaskName | Out-Null
    Write-Status "Agent started" "OK"
} catch {
    Write-Status "Could not start agent automatically" "WARN"
}

# Done
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host "  Installation Complete!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "Installation directory: $InstallDir" -ForegroundColor Gray
Write-Host "Configuration: $ConfigDir\agent.json" -ForegroundColor Gray
Write-Host "Logs: $ConfigDir\agent.log" -ForegroundColor Gray
Write-Host ""
Write-Host "The agent will start automatically on boot." -ForegroundColor Cyan
Write-Host ""
Write-Host "To check status:" -ForegroundColor Yellow
Write-Host "  schtasks /query /tn `"$TaskName`"" -ForegroundColor White
Write-Host ""
Write-Host "To view logs:" -ForegroundColor Yellow
Write-Host "  Get-Content $ConfigDir\agent.log -Tail 50" -ForegroundColor White
Write-Host ""


File created successfully at: C:/Users/MikeSwanson/claude/gururmm/agent-legacy/Install-GuruRMM.ps1

#Requires -Version 2.0
#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Installs GuruRMM Legacy Agent as a scheduled task

.DESCRIPTION
    - Copies agent to C:\Program Files\GuruRMM
    - Registers with server using site code
    - Creates scheduled task to run at startup

.PARAMETER SiteCode
    The site code (WORD-WORD-NUMBER format, e.g., DARK-GROVE-7839)

.PARAMETER ServerUrl
    The GuruRMM server URL (default: https://rmm-api.azcomputerguru.com)

.EXAMPLE
    .\Install-GuruRMM.ps1 -SiteCode DARK-GROVE-7839
#>

param(
    [Parameter()]
    [string]$SiteCode,

    [Parameter()]
    [string]$ServerUrl = "https://rmm-api.azcomputerguru.com"
)

$ErrorActionPreference = "Stop"

$InstallDir = "C:\Program Files\GuruRMM"
$ConfigDir = "C:\ProgramData\GuruRMM"
$TaskName = "GuruRMM Agent"
$AgentScript = "GuruRMM-Agent.ps1"

function Write-Status {
    param([string]$Message, [string]$Type = "INFO")
    switch ($Type) {
        "OK"    { Write-Host "[OK] $Message" -ForegroundColor Green }
        "ERROR" { Write-Host "[ERROR] $Message" -ForegroundColor Red }
        "WARN"  { Write-Host "[WARN] $Message" -ForegroundColor Yellow }
        default { Write-Host "[*] $Message" -ForegroundColor Cyan }
    }
}

# Header
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "  GuruRMM Legacy Agent Installer" -ForegroundColor Cyan
Write-Host "  For Windows Server 2008 R2 and older" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""

# Check if running as admin
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
    Write-Status "This script must be run as Administrator" "ERROR"
    exit 1
}

# Get site code if not provided
if (-not $SiteCode) {
    Write-Host "Enter site code (WORD-WORD-NUMBER format)" -ForegroundColor Yellow
    Write-Host "Example: DARK-GROVE-7839" -ForegroundColor Gray
    Write-Host ""
    $SiteCode = Read-Host "Site Code"
}

# Validate site code format
$SiteCode = $SiteCode.ToUpper().Trim()
if ($SiteCode -notmatch '^[A-Z]+-[A-Z]+-\d+$') {
    Write-Status "Invalid site code format. Expected: WORD-WORD-NUMBER" "ERROR"
    exit 1
}

Write-Status "Site Code: $SiteCode"
Write-Status "Server: $ServerUrl"
Write-Host ""

# Step 1: Create directories
Write-Status "Creating installation directories..."
try {
    if (-not (Test-Path $InstallDir)) {
        New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
    }
    if (-not (Test-Path $ConfigDir)) {
        New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null
    }
    Write-Status "Directories created" "OK"
} catch {
    Write-Status "Failed to create directories: $($_.Exception.Message)" "ERROR"
    exit 1
}

# Step 2: Copy agent script
Write-Status "Copying agent script..."
try {
    $sourceScript = Join-Path $PSScriptRoot $AgentScript
    if (-not (Test-Path $sourceScript)) {
        Write-Status "Agent script not found: $sourceScript" "ERROR"
        exit 1
    }

    $destScript = Join-Path $InstallDir $AgentScript
    Copy-Item $sourceScript $destScript -Force
    Write-Status "Agent script installed to $destScript" "OK"
} catch {
    Write-Status "Failed to copy agent: $($_.Exception.Message)" "ERROR"
    exit 1
}

# Step 3: Register agent
Write-Status "Registering with GuruRMM server..."
try {
    $registerArgs = "-ExecutionPolicy Bypass -File `"$destScript`" -SiteCode `"$SiteCode`" -ServerUrl `"$ServerUrl`""
    $process = Start-Process powershell.exe -ArgumentList $registerArgs -Wait -PassThru -NoNewWindow

    if ($process.ExitCode -ne 0) {
        Write-Status "Registration may have failed. Check connectivity to $ServerUrl" "WARN"
    } else {
        Write-Status "Agent registered successfully" "OK"
    }
} catch {
    Write-Status "Registration error: $($_.Exception.Message)" "WARN"
}

# Step 4: Remove existing scheduled task if present
Write-Status "Configuring scheduled task..."
try {
    $existingTask = schtasks /query /tn $TaskName 2>$null
    if ($existingTask) {
        schtasks /delete /tn $TaskName /f | Out-Null
        Write-Status "Removed existing task" "OK"
    }
} catch {}

# Step 5: Create scheduled task
try {
    # Create the task to run at startup and every 5 minutes
    $taskCommand = "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$destScript`""

    # Create task that runs at system startup
    schtasks /create /tn $TaskName /tr $taskCommand /sc onstart /ru SYSTEM /rl HIGHEST /f | Out-Null

    Write-Status "Scheduled task created: $TaskName" "OK"
} catch {
    Write-Status "Failed to create scheduled task: $($_.Exception.Message)" "ERROR"
    Write-Status "You may need to manually create the task" "WARN"
}

# Step 6: Start the agent now
Write-Status "Starting agent..."
try {
    schtasks /run /tn $TaskName | Out-Null
    Write-Status "Agent started" "OK"
} catch {
    Write-Status "Could not start agent automatically" "WARN"
}

# Done
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host "  Installation Complete!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "Installation directory: $InstallDir" -ForegroundColor Gray
Write-Host "Configuration: $ConfigDir\agent.json" -ForegroundColor Gray
Write-Host "Logs: $ConfigDir\agent.log" -ForegroundColor Gray
Write-Host ""
Write-Host "The agent will start automatically on boot." -ForegroundColor Cyan
Write-Host ""
Write-Host "To check status:" -ForegroundColor Yellow
Write-Host "  schtasks /query /tn `"$TaskName`"" -ForegroundColor White
Write-Host ""
Write-Host "To view logs:" -ForegroundColor Yellow
Write-Host "  Get-Content $ConfigDir\agent.log -Tail 50" -ForegroundColor White
Write-Host ""


Now let me create an uninstaller script as well for completeness.


#Requires -Version 2.0
#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Uninstalls GuruRMM Legacy Agent

.PARAMETER KeepConfig
    Keep configuration and logs (don't delete ProgramData folder)
#>

param(
    [switch]$KeepConfig
)

$InstallDir = "C:\Program Files\GuruRMM"
$ConfigDir = "C:\ProgramData\GuruRMM"
$TaskName = "GuruRMM Agent"

Write-Host ""
Write-Host "Uninstalling GuruRMM Legacy Agent..." -ForegroundColor Yellow
Write-Host ""

# Stop and remove scheduled task
try {
    schtasks /end /tn $TaskName 2>$null | Out-Null
    schtasks /delete /tn $TaskName /f 2>$null | Out-Null
    Write-Host "[OK] Scheduled task removed" -ForegroundColor Green
} catch {
    Write-Host "[WARN] Could not remove scheduled task" -ForegroundColor Yellow
}

# Remove installation directory
if (Test-Path $InstallDir) {
    try {
        Remove-Item $InstallDir -Recurse -Force
        Write-Host "[OK] Installation directory removed" -ForegroundColor Green
    } catch {
        Write-Host "[WARN] Could not remove $InstallDir" -ForegroundColor Yellow
    }
}

# Remove config (optional)
if (-not $KeepConfig -and (Test-Path $ConfigDir)) {
    try {
        Remove-Item $ConfigDir -Recurse -Force
        Write-Host "[OK] Configuration removed" -ForegroundColor Green
    } catch {
        Write-Host "[WARN] Could not remove $ConfigDir" -ForegroundColor Yellow
    }
} elseif ($KeepConfig) {
    Write-Host "[*] Configuration preserved at $ConfigDir" -ForegroundColor Cyan
}

Write-Host ""
Write-Host "Uninstall complete." -ForegroundColor Green
Write-Host ""


File created successfully at: C:/Users/MikeSwanson/claude/gururmm/agent-legacy/Uninstall-GuruRMM.ps1

#Requires -Version 2.0
#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Uninstalls GuruRMM Legacy Agent

.PARAMETER KeepConfig
    Keep configuration and logs (don't delete ProgramData folder)
#>

param(
    [switch]$KeepConfig
)

$InstallDir = "C:\Program Files\GuruRMM"
$ConfigDir = "C:\ProgramData\GuruRMM"
$TaskName = "GuruRMM Agent"

Write-Host ""
Write-Host "Uninstalling GuruRMM Legacy Agent..." -ForegroundColor Yellow
Write-Host ""

# Stop and remove scheduled task
try {
    schtasks /end /tn $TaskName 2>$null | Out-Null
    schtasks /delete /tn $TaskName /f 2>$null | Out-Null
    Write-Host "[OK] Scheduled task removed" -ForegroundColor Green
} catch {
    Write-Host "[WARN] Could not remove scheduled task" -ForegroundColor Yellow
}

# Remove installation directory
if (Test-Path $InstallDir) {
    try {
        Remove-Item $InstallDir -Recurse -Force
        Write-Host "[OK] Installation directory removed" -ForegroundColor Green
    } catch {
        Write-Host "[WARN] Could not remove $InstallDir" -ForegroundColor Yellow
    }
}

# Remove config (optional)
if (-not $KeepConfig -and (Test-Path $ConfigDir)) {
    try {
        Remove-Item $ConfigDir -Recurse -Force
        Write-Host "[OK] Configuration removed" -ForegroundColor Green
    } catch {
        Write-Host "[WARN] Could not remove $ConfigDir" -ForegroundColor Yellow
    }
} elseif ($KeepConfig) {
    Write-Host "[*] Configuration preserved at $ConfigDir" -ForegroundColor Cyan
}

Write-Host ""
Write-Host "Uninstall complete." -ForegroundColor Green
Write-Host ""


Now I need to add the server-side endpoint to handle legacy agent registration. The current server has a `/api/agents` endpoint but it needs to be extended to handle the site code registration flow.Let me check the current server API and add the necessary endpoints.


Now I need to add server-side endpoints for the legacy agent. Let me check and update the server API.


     1→//! REST API routes
     2→//!
     3→//! Provides endpoints for:
     4→//! - Agent management (registration, listing, deletion)
     5→//! - Client and site management
     6→//! - Metrics retrieval
     7→//! - Command execution
     8→//! - User authentication
     9→
    10→pub mod agents;
    11→pub mod auth;
    12→pub mod clients;
    13→pub mod commands;
    14→pub mod metrics;
    15→pub mod sites;
    16→
    17→use axum::{
    18→    routing::{delete, get, post, put},
    19→    Router,
    20→};
    21→
    22→use crate::AppState;
    23→
    24→/// Build all API routes
    25→pub fn routes() -> Router<AppState> {
    26→    Router::new()
    27→        // Authentication
    28→        .route("/auth/login", post(auth::login))
    29→        .route("/auth/register", post(auth::register))
    30→        .route("/auth/me", get(auth::me))
    31→        // Clients
    32→        .route("/clients", get(clients::list_clients))
    33→        .route("/clients", post(clients::create_client))
    34→        .route("/clients/:id", get(clients::get_client))
    35→        .route("/clients/:id", put(clients::update_client))
    36→        .route("/clients/:id", delete(clients::delete_client))
    37→        .route("/clients/:id/sites", get(sites::list_sites_by_client))
    38→        // Sites
    39→        .route("/sites", get(sites::list_sites))
    40→        .route("/sites", post(sites::create_site))
    41→        .route("/sites/:id", get(sites::get_site))
    42→        .route("/sites/:id", put(sites::update_site))
    43→        .route("/sites/:id", delete(sites::delete_site))
    44→        .route("/sites/:id/regenerate-key", post(sites::regenerate_api_key))
    45→        // Agents
    46→        .route("/agents", get(agents::list_agents_with_details))
    47→        .route("/agents", post(agents::register_agent))
    48→        .route("/agents/stats", get(agents::get_stats))
    49→        .route("/agents/unassigned", get(agents::list_unassigned_agents))
    50→        .route("/agents/:id", get(agents::get_agent))
    51→        .route("/agents/:id", delete(agents::delete_agent))
    52→        .route("/agents/:id/move", post(agents::move_agent))
    53→        .route("/agents/:id/state", get(agents::get_agent_state))
    54→        // Metrics
    55→        .route("/agents/:id/metrics", get(metrics::get_agent_metrics))
    56→        .route("/metrics/summary", get(metrics::get_summary))
    57→        // Commands
    58→        .route("/agents/:id/command", post(commands::send_command))
    59→        .route("/commands", get(commands::list_commands))
    60→        .route("/commands/:id", get(commands::get_command))
    61→}
    62→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//! REST API routes
//!
//! Provides endpoints for:
//! - Agent management (registration, listing, deletion)
//! - Client and site management
//! - Metrics retrieval
//! - Command execution
//! - User authentication

pub mod agents;
pub mod auth;
pub mod clients;
pub mod commands;
pub mod metrics;
pub mod sites;

use axum::{
    routing::{delete, get, post, put},
    Router,
};

use crate::AppState;

/// Build all API routes
pub fn routes() -> Router<AppState> {
    Router::new()
        // Authentication
        .route("/auth/login", post(auth::login))
        .route("/auth/register", post(auth::register))
        .route("/auth/me", get(auth::me))
        // Clients
        .route("/clients", get(clients::list_clients))
        .route("/clients", post(clients::create_client))
        .route("/clients/:id", get(clients::get_client))
        .route("/clients/:id", put(clients::update_client))
        .route("/clients/:id", delete(clients::delete_client))
        .route("/clients/:id/sites", get(sites::list_sites_by_client))
        // Sites
        .route("/sites", get(sites::list_sites))
        .route("/sites", post(sites::create_site))
        .route("/sites/:id", get(sites::get_site))
        .route("/sites/:id", put(sites::update_site))
        .route("/sites/:id", delete(sites::delete_site))
        .route("/sites/:id/regenerate-key", post(sites::regenerate_api_key))
        // Agents
        .route("/agents", get(agents::list_agents_with_details))
        .route("/agents", post(agents::register_agent))
        .route("/agents/stats", get(agents::get_stats))
        .route("/agents/unassigned", get(agents::list_unassigned_agents))
        .route("/agents/:id", get(agents::get_agent))
        .route("/agents/:id", delete(agents::delete_agent))
        .route("/agents/:id/move", post(agents::move_agent))
        .route("/agents/:id/state", get(agents::get_agent_state))
        // Metrics
        .route("/agents/:id/metrics", get(metrics::get_agent_metrics))
        .route("/metrics/summary", get(metrics::get_summary))
        // Commands
        .route("/agents/:id/command", post(commands::send_command))
        .route("/commands", get(commands::list_commands))
        .route("/commands/:id", get(commands::get_command))
}


I need to add endpoints for the legacy agent:
1. `/api/agent/register-legacy` - Register with site code
2. `/api/agent/heartbeat` - Send heartbeat with system info
3. `/api/agent/command-result` - Report command execution results

Let me add these to the agents.rs file.