Install-MakeMeAdminCLI.ps1
|
#Requires -Version 5.1 #Requires -RunAsAdministrator <# .SYNOPSIS Installs the MakeMeAdminCLI module and service. .DESCRIPTION This script performs a complete installation of MakeMeAdminCLI: - Copies the module to the system-wide PowerShell modules directory - Creates the state directory for tracking active elevated users - Registers the Windows Event Log source - Creates and starts a scheduled task that runs the service as SYSTEM This script must be run as Administrator. .PARAMETER Force Forces installation even if MakeMeAdminCLI is already installed. .EXAMPLE .\Install-MakeMeAdminCLI.ps1 Performs a standard installation. .EXAMPLE .\Install-MakeMeAdminCLI.ps1 -Force Reinstalls MakeMeAdminCLI, overwriting existing files. .NOTES Author: MakeMeAdminCLI Version: 1.0.0 Installation paths: - Module: $env:ProgramFiles\WindowsPowerShell\Modules\MakeMeAdminCLI\ - State: $env:ProgramData\MakeMeAdminCLI\ - Config: $env:ProgramData\MakeMeAdminCLI\config.json #> [CmdletBinding()] param( [switch]$Force ) $ErrorActionPreference = 'Stop' #region Helper Functions function Write-Status { param( [string]$Status, [string]$Message, [ConsoleColor]$Color = [ConsoleColor]::White ) $statusColor = switch ($Status) { 'OK' { [ConsoleColor]::Green } 'WARN' { [ConsoleColor]::Yellow } 'FAIL' { [ConsoleColor]::Red } 'INFO' { [ConsoleColor]::Cyan } default { [ConsoleColor]::White } } Write-Host "[" -NoNewline Write-Host $Status -ForegroundColor $statusColor -NoNewline Write-Host "] " -NoNewline Write-Host $Message -ForegroundColor $Color } function Test-IsAdministrator { $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object System.Security.Principal.WindowsPrincipal($identity) return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) } #endregion #region Main Script # Verify running as Administrator if (-not (Test-IsAdministrator)) { Write-Host "" Write-Host "ERROR: This script must be run as Administrator." -ForegroundColor Red Write-Host "" Write-Host "Please run PowerShell as Administrator and try again." -ForegroundColor Yellow Write-Host "" exit 1 } # Define paths $ScriptRoot = Split-Path -Parent $PSCommandPath $ModuleName = "MakeMeAdminCLI" $TargetModulePath = Join-Path $env:ProgramFiles "WindowsPowerShell\Modules\$ModuleName" $StateDirectory = Join-Path $env:ProgramData $ModuleName $ConfigFilePath = Join-Path $StateDirectory "config.json" $TaskName = "MakeMeAdminCLI-Service" $TaskPath = "\Microsoft\Windows\MakeMeAdminCLI\" $EventLogSource = "MakeMeAdminCLI" Write-Host "" Write-Host "Installing MakeMeAdminCLI..." -ForegroundColor Cyan Write-Host "==============================" -ForegroundColor Cyan Write-Host "" # Check for existing installation if ((Test-Path $TargetModulePath) -and -not $Force) { Write-Status -Status "WARN" -Message "MakeMeAdminCLI is already installed at $TargetModulePath" Write-Host "" Write-Host "Use -Force to reinstall, or run Uninstall-MakeMeAdminCLI.ps1 first." -ForegroundColor Yellow Write-Host "" exit 0 } # Stop existing service if running try { $existingTask = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue if ($existingTask) { Write-Status -Status "INFO" -Message "Stopping existing service..." Stop-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue Start-Sleep -Seconds 2 } } catch { # Task doesn't exist, continue } # Step 1: Copy module to system-wide location try { if (Test-Path $TargetModulePath) { Remove-Item -Path $TargetModulePath -Recurse -Force } # Create the target directory New-Item -ItemType Directory -Path $TargetModulePath -Force | Out-Null # Copy all module files $itemsToCopy = @( "MakeMeAdminCLI.psd1", "MakeMeAdminCLI.psm1", "config.json", "Private", "Public" ) foreach ($item in $itemsToCopy) { $sourcePath = Join-Path $ScriptRoot $item if (Test-Path $sourcePath) { Copy-Item -Path $sourcePath -Destination $TargetModulePath -Recurse -Force } } Write-Status -Status "OK" -Message "Copied module to $TargetModulePath" } catch { Write-Status -Status "FAIL" -Message "Failed to copy module: $($_.Exception.Message)" exit 1 } # Step 2: Create state directory try { if (-not (Test-Path $StateDirectory)) { New-Item -ItemType Directory -Path $StateDirectory -Force | Out-Null } # Copy default config to state directory if it doesn't exist or Force is specified $sourceConfig = Join-Path $ScriptRoot "config.json" if ((Test-Path $sourceConfig) -and (-not (Test-Path $ConfigFilePath) -or $Force)) { Copy-Item -Path $sourceConfig -Destination $ConfigFilePath -Force } # Initialize state file $stateFilePath = Join-Path $StateDirectory "state.json" if (-not (Test-Path $stateFilePath)) { $initialState = @{ ActiveUsers = @() LastUpdated = (Get-Date).ToString('o') ServiceStartTime = $null } $initialState | ConvertTo-Json -Depth 10 | Set-Content -Path $stateFilePath -Encoding UTF8 -Force } Write-Status -Status "OK" -Message "Created state directory at $StateDirectory" } catch { Write-Status -Status "FAIL" -Message "Failed to create state directory: $($_.Exception.Message)" exit 1 } # Step 3: Register Event Log source try { if (-not [System.Diagnostics.EventLog]::SourceExists($EventLogSource)) { [System.Diagnostics.EventLog]::CreateEventSource($EventLogSource, "Application") Start-Sleep -Milliseconds 500 # Brief pause for registration to complete } Write-Status -Status "OK" -Message "Registered Event Log source '$EventLogSource'" } catch { Write-Status -Status "WARN" -Message "Could not register Event Log source: $($_.Exception.Message)" # Non-fatal - continue with installation } # Step 4: Create scheduled task for the service try { $servicePath = Join-Path $TargetModulePath "Private\Service-Main.ps1" # Create the task folder if it doesn't exist $taskFolderPath = $TaskPath $schedule = New-Object -ComObject Schedule.Service $schedule.Connect() $rootFolder = $schedule.GetFolder("\") try { $null = $schedule.GetFolder($taskFolderPath) } catch { # Create the folder hierarchy $pathParts = $taskFolderPath.Trim('\').Split('\') $currentPath = "\" foreach ($part in $pathParts) { $nextPath = "$currentPath$part" try { $null = $schedule.GetFolder($nextPath) } catch { $parentFolder = $schedule.GetFolder($currentPath.TrimEnd('\')) $null = $parentFolder.CreateFolder($part) } $currentPath = "$nextPath\" } } # Remove existing task if present $existingTask = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -ErrorAction SilentlyContinue if ($existingTask) { Unregister-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath -Confirm:$false } # Create the scheduled task $action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -NonInteractive -File `"$servicePath`"" # Trigger: At system startup $trigger = New-ScheduledTaskTrigger -AtStartup # Principal: Run as SYSTEM with highest privileges $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" ` -LogonType ServiceAccount ` -RunLevel Highest # Settings $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries ` -DontStopIfGoingOnBatteries ` -StartWhenAvailable ` -RestartCount 3 ` -RestartInterval (New-TimeSpan -Minutes 1) ` -ExecutionTimeLimit (New-TimeSpan -Days 365) ` -Priority 4 ` -Hidden # Register the task $task = Register-ScheduledTask -TaskName $TaskName ` -TaskPath $TaskPath ` -Action $action ` -Trigger $trigger ` -Principal $principal ` -Settings $settings ` -Description "MakeMeAdminCLI service - Provides temporary administrator rights to users" Write-Status -Status "OK" -Message "Created service scheduled task" } catch { Write-Status -Status "FAIL" -Message "Failed to create scheduled task: $($_.Exception.Message)" exit 1 } # Step 5: Start the service try { Start-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath # Wait briefly for the service to start Start-Sleep -Seconds 2 $taskInfo = Get-ScheduledTask -TaskName $TaskName -TaskPath $TaskPath if ($taskInfo.State -eq 'Running') { Write-Status -Status "OK" -Message "Started MakeMeAdminCLI service" } else { Write-Status -Status "WARN" -Message "Service started but may not be running (State: $($taskInfo.State))" } } catch { Write-Status -Status "WARN" -Message "Could not start service immediately: $($_.Exception.Message)" Write-Host " The service will start automatically on next boot." -ForegroundColor Yellow } # Installation complete Write-Host "" Write-Host "Installation complete." -ForegroundColor Green Write-Host "" Write-Host "Users can now run:" -ForegroundColor White Write-Host " Add-TempAdmin # Request temporary admin rights" -ForegroundColor Gray Write-Host " Get-TempAdminStatus # Check current status" -ForegroundColor Gray Write-Host " Remove-TempAdmin # Remove admin rights early" -ForegroundColor Gray Write-Host "" Write-Host "Configure with: Set-TempAdminConfig (requires elevation)" -ForegroundColor Yellow Write-Host "" #endregion |