From 2adfef238132fe475934b8d6e774a122b907a9fd Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Sat, 20 Dec 2025 03:17:08 +0400 Subject: [PATCH 1/4] adding powershell --- Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Dockerfile b/Dockerfile index 02686bf..08ab9cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,6 +39,10 @@ RUN apt update && apt install -y \ && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \ && apt update \ && apt install -y google-chrome-stable \ + && POWERSHELL_VERSION=$(wget -qO- https://api.github.com/repos/PowerShell/PowerShell/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/v//') \ + && wget -q -O /tmp/powershell.deb "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/powershell_${POWERSHELL_VERSION}-1.deb_amd64.deb" \ + && apt install -y /tmp/powershell.deb \ + && rm /tmp/powershell.deb \ && apt clean \ && rm -rf /var/lib/apt/lists/* From ccce5d24c1ddd33977b83f19eb0a24a29ab8a369 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Sat, 20 Dec 2025 17:54:06 +0400 Subject: [PATCH 2/4] improving setup docs for windows --- README.md | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 47600a2..9dfb835 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,24 @@ ### Installation #### Go Install + +**Note:** Go must be pre-installed on your system. Download Go from [golang.org](https://golang.org/dl/) if needed. + ```bash go install github.com/projectdiscovery/pd-agent/cmd/pd-agent@latest ``` +The binary will be installed in your Go bin directory: +- **Linux/macOS:** `$HOME/go/bin/pd-agent` (or `$GOPATH/bin/pd-agent` if `GOPATH` is set) +- **Windows:** `%USERPROFILE%\go\bin\pd-agent.exe` (or `%GOPATH%\bin\pd-agent.exe` if `GOPATH` is set) + +Ensure this directory is in your PATH to run `pd-agent` (or `pd-agent.exe` on Windows) from anywhere. + #### Docker + +**Note:** Docker must be installed on your system. Download Docker from [docker.com](https://www.docker.com/products/docker-desktop/) if needed. + +**Linux/macOS:** ```bash docker run -d --name pd-agent \ --network host --cap-add NET_RAW --cap-add NET_ADMIN \ @@ -45,6 +58,17 @@ docker run -d --name pd-agent \ -agent-tags production ``` +**Windows (Docker Desktop):** +```powershell +docker run -d --name pd-agent \ + -e PDCP_API_KEY=your-api-key \ + -e PDCP_TEAM_ID=your-team-id \ + projectdiscovery/pd-agent:latest \ + -agent-tags production +``` + +**Note:** On Windows, `--network host` and `--cap-add` flags are not supported by Docker Desktop. Only passive discovery features are affected; all other agent functionality works normally. + #### Kubernetes ```bash # Create namespace @@ -70,7 +94,7 @@ The agent automatically discovers Kubernetes cluster subnets (nodes, pods, servi The agent automatically discovers local network subnets and reports them to the platform: - **Local networks:** Discovers private IP ranges from network interfaces and routing tables - **Kubernetes:** Automatically discovers and aggregates cluster subnets (node IPs, pod CIDRs, service CIDRs) -- **Docker:** Use `--network host` and network capabilities (`NET_RAW`, `NET_ADMIN`) to enable discovery +- **Docker (Linux/macOS only):** Use `--network host` and network capabilities (`NET_RAW`, `NET_ADMIN`) to enable passive discovery. On Windows Docker Desktop, these flags are not supported and passive discovery will be unavailable. For Kubernetes deployments, the agent requires `ClusterRole` permissions to discover cluster resources (included in the deployment manifest). @@ -84,13 +108,71 @@ For Kubernetes deployments, the agent requires `ClusterRole` permissions to disc | `PDCP_AGENT_TAGS` | No | - | Comma-separated agent tags | | `PDCP_AGENT_NAME` | No | Hostname | Agent display name | +#### Setting Environment Variables + +**Linux/macOS (bash/zsh):** +```bash +export PDCP_API_KEY=your-api-key +export PDCP_TEAM_ID=your-team-id +export PDCP_AGENT_TAGS=production,staging +``` + +**Windows (PowerShell):** +```powershell +$env:PDCP_API_KEY="your-api-key" +$env:PDCP_TEAM_ID="your-team-id" +$env:PDCP_AGENT_TAGS="production,staging" +``` + +**Windows (Command Prompt):** +```cmd +set PDCP_API_KEY=your-api-key +set PDCP_TEAM_ID=your-team-id +set PDCP_AGENT_TAGS=production,staging +``` + +**Windows (Permanent - System Environment Variables):** +1. Open System Properties → Advanced → Environment Variables +2. Add variables under User or System variables +3. Restart the terminal/service for changes to take effect + +**Windows (WSL2):** +Follow Linux instructions - WSL2 uses Linux environment variable syntax. + ### Usage +**Linux/macOS:** ```bash # Basic usage pd-agent -agent-networks internal + +# With tags +pd-agent -agent-tags production,staging -agent-networks internal ``` +**Windows (PowerShell):** +```powershell +# Basic usage +pd-agent.exe -agent-networks internal + +# With tags +pd-agent.exe -agent-tags production,staging -agent-networks internal +``` + +**Windows (Command Prompt):** +```cmd +# Basic usage +pd-agent.exe -agent-networks internal + +# With tags +pd-agent.exe -agent-tags production,staging -agent-networks internal +``` + +**Windows (WSL2):** +Follow Linux instructions - commands are identical to Linux/macOS. + +**Note:** On Windows, if `pd-agent.exe` is in your PATH, you can use `pd-agent` instead of `pd-agent.exe`. The `.exe` extension is optional in PowerShell and required in Command Prompt. + ### Configuration The agent uses environment variables or command-line flags for configuration. See the Environment Variables table above for all available options. @@ -125,13 +207,48 @@ The agent uses environment variables or command-line flags for configuration. Se #### Enable Verbose Logging -Add `-verbose` flag or set environment variable: +**Linux/macOS:** ```bash +# Using flag +pd-agent -verbose + +# Using environment variable export PDCP_VERBOSE=true -# or +pd-agent ... + +# Or inline PDCP_VERBOSE=1 pd-agent ... ``` +**Windows (PowerShell):** +```powershell +# Using flag +pd-agent.exe -verbose + +# Using environment variable +$env:PDCP_VERBOSE="true" +pd-agent.exe ... + +# Or inline +$env:PDCP_VERBOSE="1"; pd-agent.exe ... +``` + +**Windows (Command Prompt):** +```cmd +# Using flag +pd-agent.exe -verbose + +# Using environment variable +set PDCP_VERBOSE=true +pd-agent.exe ... + +# Or inline (requires separate commands) +set PDCP_VERBOSE=1 && pd-agent.exe ... +``` + +**Windows (WSL2):** +Follow Linux instructions - use Linux syntax. + ### Best Practices 1. **Agent Tagging:** Use descriptive tags to organize agents (e.g., `production`, `staging`, `scanner-1`) @@ -147,17 +264,32 @@ PDCP_VERBOSE=1 pd-agent ... #### Custom Proxy Configuration -Configure a custom proxy for agent communication: - +**Linux/macOS:** ```bash export PROXY_URL=http://proxy.example.com:8080 pd-agent -verbose ``` +**Windows (PowerShell):** +```powershell +$env:PROXY_URL="http://proxy.example.com:8080" +pd-agent.exe -verbose +``` + +**Windows (Command Prompt):** +```cmd +set PROXY_URL=http://proxy.example.com:8080 +pd-agent.exe -verbose +``` + +**Windows (WSL2):** +Follow Linux instructions - use Linux syntax. + #### Agent Grouping Use tags and networks to group agents: +**Linux/macOS:** ```bash # Production agents pd-agent -agent-tags production,us-east -agent-networks prod-network @@ -166,6 +298,18 @@ pd-agent -agent-tags production,us-east -agent-networks prod-network pd-agent -agent-tags staging,us-west -agent-networks staging-network ``` +**Windows (PowerShell/Command Prompt):** +```powershell +# Production agents +pd-agent.exe -agent-tags production,us-east -agent-networks prod-network + +# Staging agents +pd-agent.exe -agent-tags staging,us-west -agent-networks staging-network +``` + +**Windows (WSL2):** +Follow Linux instructions - commands are identical to Linux/macOS. + --------
From 859bec91f9d16a611875731b17314ddaacfeb5d1 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Sat, 20 Dec 2025 18:40:44 +0400 Subject: [PATCH 3/4] adding docker execution notes --- Dockerfile | 2 ++ README.md | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Dockerfile b/Dockerfile index 08ab9cb..708817b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,8 @@ RUN apt update && apt install -y \ ca-certificates \ nmap \ libpcap-dev \ + python3 \ + python3-pip \ wget \ gnupg \ && wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg \ diff --git a/README.md b/README.md index 9dfb835..eb60da7 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,16 @@ The binary will be installed in your Go bin directory: Ensure this directory is in your PATH to run `pd-agent` (or `pd-agent.exe` on Windows) from anywhere. +#### Download Binary from Releases + +Pre-built binaries are available for download from the [GitHub releases page](https://github.com/projectdiscovery/pd-agent/releases). Download the appropriate binary for your platform: + +- **Linux:** Download the `pd-agent-linux-amd64.tar.gz` archive, extract it, and place the `pd-agent` binary in a directory that is in your PATH (e.g., `/usr/local/bin`). +- **macOS:** Download the `pd-agent-macos-amd64.tar.gz` archive, extract it, and place the `pd-agent` binary in a directory that is in your PATH (e.g., `/usr/local/bin`). +- **Windows:** Download the `pd-agent-windows-amd64.exe` binary and place it in a directory that is in your PATH (e.g., `C:\Program Files\pd-agent`). + +Ensure the binary directory is in your system PATH to run `pd-agent` from anywhere. + #### Docker **Note:** Docker must be installed on your system. Download Docker from [docker.com](https://www.docker.com/products/docker-desktop/) if needed. @@ -310,6 +320,15 @@ pd-agent.exe -agent-tags staging,us-west -agent-networks staging-network **Windows (WSL2):** Follow Linux instructions - commands are identical to Linux/macOS. +### Docker Execution Note + +The Docker container comes with all prerequisites pre-installed and updated, requiring no further setup: + +- **ProjectDiscovery Tools:** dnsx, naabu, httpx, tlsx, nuclei +- **System Tools:** nmap, Chrome (headless), PowerShell + +Nuclei executes headless templates and code templates automatically when the system has sufficient resources. Code templates can execute with the following interpreters on any platform: **bash**, **python**, and **PowerShell**. + --------
From 82bd4f0f9d367ec79728d5b50534597b778d2392 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Sun, 21 Dec 2025 23:25:33 +0400 Subject: [PATCH 4/4] adding powershell scripts --- README.md | 50 ++++ setup.agent.ps1 | 398 +++++++++++++++++++++++++++ setup.go.ps1 | 427 +++++++++++++++++++++++++++++ setup.windows.desktop.ps1 | 356 ++++++++++++++++++++++++ setup.windows.server.ps1 | 561 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1792 insertions(+) create mode 100644 setup.agent.ps1 create mode 100644 setup.go.ps1 create mode 100644 setup.windows.desktop.ps1 create mode 100644 setup.windows.server.ps1 diff --git a/README.md b/README.md index eb60da7..611266a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ **Note:** Go must be pre-installed on your system. Download Go from [golang.org](https://golang.org/dl/) if needed. +**Windows users:** You can use the automated setup script `setup.go.ps1` to install/update Go automatically. + ```bash go install github.com/projectdiscovery/pd-agent/cmd/pd-agent@latest ``` @@ -58,6 +60,10 @@ Ensure the binary directory is in your system PATH to run `pd-agent` from anywhe **Note:** Docker must be installed on your system. Download Docker from [docker.com](https://www.docker.com/products/docker-desktop/) if needed. +**Windows users:** You can use the automated setup scripts: +- `setup.windows.desktop.ps1` for Windows 10/11 (Docker Desktop) +- `setup.windows.server.ps1` for Windows Server (Docker Engine from binaries) + **Linux/macOS:** ```bash docker run -d --name pd-agent \ @@ -79,6 +85,50 @@ docker run -d --name pd-agent \ **Note:** On Windows, `--network host` and `--cap-add` flags are not supported by Docker Desktop. Only passive discovery features are affected; all other agent functionality works normally. +#### Automated Windows Setup Scripts + +We provide PowerShell scripts to automate the installation of prerequisites and the pd-agent on Windows: + +**Windows Desktop (Windows 10/11):** +```powershell +# Install Docker Desktop +.\setup.windows.desktop.ps1 + +# Install Go (optional) +.\setup.go.ps1 + +# Deploy pd-agent (installs Docker if needed, pulls image, and starts container) +.\setup.agent.ps1 +``` + +**Windows Server:** +```powershell +# Install Docker Engine from binaries (includes Windows Containers feature) +.\setup.windows.server.ps1 + +# Install Go (optional) +.\setup.go.ps1 + +# Deploy pd-agent (installs Docker if needed, pulls image, and starts container) +.\setup.agent.ps1 +``` + +**All-in-one deployment:** +```powershell +# Deploy pd-agent with automatic Docker and Go installation +.\setup.agent.ps1 -InstallGo +``` + +**Features:** +- Automatic Docker installation/update (Desktop or Server) +- Optional Go installation/update +- Automatic pd-agent image pull and container deployment +- Preserves existing container configuration (env vars, volumes, commands) +- Only updates if newer versions are available +- Fully automated and silent installation + +**Note:** All scripts require Administrator privileges. Run PowerShell as Administrator before executing. + #### Kubernetes ```bash # Create namespace diff --git a/setup.agent.ps1 b/setup.agent.ps1 new file mode 100644 index 0000000..baad04a --- /dev/null +++ b/setup.agent.ps1 @@ -0,0 +1,398 @@ +#Requires -RunAsAdministrator + +<# +.SYNOPSIS + Automated deployment and update script for pd-agent Docker container + +.DESCRIPTION + This script: + - Installs or updates Docker Desktop + - Optionally installs Go + - Pulls the latest pd-agent Docker image + - Stops idle running containers + - Starts a new container with the same configuration + - Skips update if image is already up to date +#> + +param( + [switch]$InstallGo, + [string]$ContainerName = "pd-agent", + [string]$ImageName = "projectdiscovery/pd-agent:latest" +) + +$ErrorActionPreference = "Stop" + +# Check if running as Administrator +$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $isAdmin) { + Write-Error "This script must be run as Administrator. Please run PowerShell as Administrator and try again." + exit 1 +} + +Write-Host "=== PD-Agent Deployment Script ===" -ForegroundColor Cyan +Write-Host "" + +# Function to test if Docker is running +function Test-DockerRunning { + try { + $null = docker version 2>$null + return ($LASTEXITCODE -eq 0) + } catch { + return $false + } +} + +# Function to wait for Docker daemon +function Wait-Docker { + Write-Host "Waiting for Docker daemon..." -ForegroundColor Cyan + $maxWaitMinutes = 2 + $startTime = Get-Date + + while (-not (Test-DockerRunning)) { + $timeElapsed = $(Get-Date) - $startTime + if ($timeElapsed.TotalMinutes -ge $maxWaitMinutes) { + throw "Docker daemon did not become ready within $maxWaitMinutes minutes." + } + Start-Sleep -Seconds 2 + } + Write-Host "Docker daemon is ready." -ForegroundColor Green +} + +# Install or update Docker +Write-Host "[1/5] Checking Docker installation..." -ForegroundColor Cyan + +# Try to detect if we're on Windows Server or Desktop +$isWindowsServer = $false +try { + $osInfo = Get-CimInstance -ClassName Win32_OperatingSystem + if ($osInfo.ProductType -eq 3) { + $isWindowsServer = $true + } +} catch { + # Assume desktop if we can't determine +} + +# Choose appropriate Docker setup script +if ($isWindowsServer) { + $dockerScript = Join-Path $PSScriptRoot "setup.windows.server.ps1" + Write-Host "Detected Windows Server. Using server binary installation." -ForegroundColor Gray +} else { + $dockerScript = Join-Path $PSScriptRoot "setup.windows.desktop.ps1" + Write-Host "Detected Windows Desktop. Using Desktop installation." -ForegroundColor Gray +} + +if (Test-Path $dockerScript) { + Write-Host "Running Docker setup script..." -ForegroundColor Gray + & $dockerScript + if ($LASTEXITCODE -ne 0) { + Write-Error "Docker setup failed. Please check the Docker setup script." + exit 1 + } +} else { + Write-Warning "Docker setup script not found at $dockerScript" + Write-Host "Checking if Docker is installed..." -ForegroundColor Cyan + if (-not (Test-DockerRunning)) { + Write-Error "Docker is not running. Please install Docker first or ensure the Docker setup script is in the same directory." + exit 1 + } +} + +Wait-Docker +Write-Host "Docker is ready." -ForegroundColor Green +Write-Host "" + +# Optionally install Go +if ($InstallGo) { + Write-Host "[2/5] Installing/updating Go..." -ForegroundColor Cyan + $goScript = Join-Path $PSScriptRoot "setup.go.ps1" + if (Test-Path $goScript) { + Write-Host "Running Go setup script..." -ForegroundColor Gray + & $goScript + if ($LASTEXITCODE -ne 0) { + Write-Warning "Go setup had issues, but continuing with Docker deployment..." + } + } else { + Write-Warning "Go setup script not found at $goScript" + } + Write-Host "" +} else { + Write-Host "[2/5] Skipping Go installation (use -InstallGo to enable)" -ForegroundColor Gray + Write-Host "" +} + +# Check current image version +Write-Host "[3/5] Checking current Docker image..." -ForegroundColor Cyan +$currentImageId = $null +$imageExists = $false + +try { + $imageInfo = docker images $ImageName --format "{{.ID}}" 2>$null + if ($imageInfo) { + $imageExists = $true + $currentImageId = $imageInfo.Trim() + Write-Host "Current image ID: $currentImageId" -ForegroundColor Gray + } +} catch { + Write-Host "No local image found." -ForegroundColor Gray +} + +# Pull latest image +Write-Host "[4/5] Pulling latest pd-agent image..." -ForegroundColor Cyan +try { + docker pull $ImageName + if ($LASTEXITCODE -ne 0) { + throw "Failed to pull Docker image" + } + + $newImageId = (docker images $ImageName --format "{{.ID}}" 2>$null).Trim() + + if ($imageExists -and $currentImageId -eq $newImageId) { + Write-Host "Image is already up to date (ID: $newImageId)" -ForegroundColor Green + Write-Host "Skipping container restart." -ForegroundColor Yellow + Write-Host "" + Write-Host "=== Deployment completed - no changes needed ===" -ForegroundColor Green + exit 0 + } else { + if ($imageExists) { + Write-Host "New image pulled (old: $currentImageId, new: $newImageId)" -ForegroundColor Green + } else { + Write-Host "Image pulled successfully (ID: $newImageId)" -ForegroundColor Green + } + } +} catch { + Write-Error "Failed to pull Docker image: $_" + exit 1 +} +Write-Host "" + +# Handle existing container +Write-Host "[5/5] Managing container..." -ForegroundColor Cyan + +$containerExists = $false +$containerRunning = $false +$containerId = $null + +try { + $containerInfo = docker ps -a --filter "name=$ContainerName" --format "{{.ID}}|{{.Status}}" 2>$null + if ($containerInfo) { + $containerExists = $true + $parts = $containerInfo -split '\|' + $containerId = $parts[0] + $status = $parts[1] + + if ($status -like "*Up*") { + $containerRunning = $true + Write-Host "Container '$ContainerName' is running (ID: $containerId)" -ForegroundColor Yellow + } else { + Write-Host "Container '$ContainerName' exists but is stopped (ID: $containerId)" -ForegroundColor Gray + } + } +} catch { + Write-Host "No existing container found." -ForegroundColor Gray +} + +# Get original container configuration BEFORE stopping/removing +Write-Host "Retrieving original container configuration..." -ForegroundColor Cyan + +$envVars = @() +$volumes = @() +$networkMode = $null +$capAdd = @() +$command = @() +$restartPolicy = $null + +if ($containerId) { + try { + # Get container configuration before we stop/remove it + $inspectOutput = docker inspect $ContainerName 2>$null + if ($inspectOutput) { + $inspect = $inspectOutput | ConvertFrom-Json + if ($inspect) { + # Get environment variables + if ($inspect[0].Config.Env) { + $envVars = $inspect[0].Config.Env + } + + # Get volumes + if ($inspect[0].Mounts) { + foreach ($mount in $inspect[0].Mounts) { + if ($mount.Type -eq "volume" -or $mount.Type -eq "bind") { + $volumes += "${($mount.Source)}:$($mount.Destination)" + } + } + } + + # Get network mode + if ($inspect[0].HostConfig.NetworkMode) { + $networkMode = $inspect[0].HostConfig.NetworkMode + } + + # Get capabilities + if ($inspect[0].HostConfig.CapAdd) { + $capAdd = $inspect[0].HostConfig.CapAdd + } + + # Get restart policy + if ($inspect[0].HostConfig.RestartPolicy.Name) { + $restartPolicy = $inspect[0].HostConfig.RestartPolicy.Name + } + + # Get command/args + if ($inspect[0].Config.Cmd) { + $command = $inspect[0].Config.Cmd + } + if ($inspect[0].Config.Entrypoint) { + $entrypoint = $inspect[0].Config.Entrypoint + } + } + } + } catch { + Write-Warning "Could not retrieve full container configuration: $_" + Write-Host "Will use default configuration." -ForegroundColor Yellow + } +} + +# Stop and remove existing container +if ($containerRunning) { + # Check if container is idle (low CPU/memory usage) + Write-Host "Checking if container is idle..." -ForegroundColor Cyan + $isIdle = $false + + try { + $stats = docker stats $ContainerName --no-stream --format "{{.CPUPerc}}|{{.MemUsage}}" 2>$null + if ($stats) { + $parts = $stats -split '\|' + $cpuPerc = $parts[0] -replace '%', '' + $memUsage = $parts[1] + + Write-Host "Container stats - CPU: $cpuPerc%, Memory: $memUsage" -ForegroundColor Gray + + # Consider idle if CPU < 1% (can be adjusted) + if ([double]$cpuPerc -lt 1.0) { + $isIdle = $true + Write-Host "Container appears to be idle (CPU < 1%)." -ForegroundColor Yellow + } else { + Write-Host "Container is active (CPU: $cpuPerc%)." -ForegroundColor Yellow + } + } else { + Write-Warning "Could not get container stats. Assuming container is active." + } + } catch { + Write-Warning "Could not check container stats: $_" + Write-Warning "Assuming container is active." + } + + # Stop container for update + if ($isIdle) { + Write-Host "Stopping idle container for update..." -ForegroundColor Yellow + } else { + Write-Host "Stopping active container for update..." -ForegroundColor Yellow + } + docker stop $ContainerName + Start-Sleep -Seconds 2 +} + +if ($containerExists) { + Write-Host "Removing old container..." -ForegroundColor Cyan + docker rm $ContainerName 2>$null + if ($LASTEXITCODE -eq 0) { + Write-Host "Old container removed." -ForegroundColor Green + } + Start-Sleep -Seconds 1 +} + +# Build docker run command and start new container +Write-Host "Starting new container with configuration..." -ForegroundColor Cyan + +$dockerArgs = @("run", "-d", "--name", $ContainerName) + +# Add restart policy +if ($restartPolicy) { + $dockerArgs += "--restart" + $dockerArgs += $restartPolicy +} else { + $dockerArgs += "--restart" + $dockerArgs += "unless-stopped" +} + +# Add environment variables +if ($envVars.Count -gt 0) { + foreach ($env in $envVars) { + $dockerArgs += "-e" + $dockerArgs += $env + } +} else { + # Default environment variables if none found + Write-Host "No environment variables found. Using defaults." -ForegroundColor Yellow + Write-Host "Note: You may need to set PDCP_API_KEY and PDCP_TEAM_ID manually." -ForegroundColor Yellow +} + +# Add volumes +if ($volumes.Count -gt 0) { + foreach ($vol in $volumes) { + $dockerArgs += "-v" + $dockerArgs += $vol + } +} + +# Add network mode (Windows doesn't support host mode, so skip it) +if ($networkMode -and $networkMode -ne "host") { + $dockerArgs += "--network" + $dockerArgs += $networkMode +} + +# Add capabilities (Windows doesn't support these, but include for compatibility) +if ($capAdd.Count -gt 0 -and $IsLinux) { + foreach ($cap in $capAdd) { + $dockerArgs += "--cap-add" + $dockerArgs += $cap + } +} + +# Add image +$dockerArgs += $ImageName + +# Add command/arguments +if ($command.Count -gt 0) { + $dockerArgs += $command +} + +# Execute docker run +Write-Host "Executing: docker $($dockerArgs -join ' ')" -ForegroundColor Gray +try { + $newContainerId = docker $dockerArgs 2>&1 + if ($LASTEXITCODE -ne 0) { + throw "Failed to start container: $newContainerId" + } + + $newContainerId = $newContainerId.Trim() + Write-Host "Container started successfully (ID: $newContainerId)" -ForegroundColor Green + + # Wait a moment and verify + Start-Sleep -Seconds 2 + $status = docker ps --filter "name=$ContainerName" --format "{{.Status}}" 2>$null + if ($status) { + Write-Host "Container status: $status" -ForegroundColor Green + } + +} catch { + Write-Error "Failed to start container: $_" + Write-Host "" + Write-Host "You may need to manually start the container with:" -ForegroundColor Yellow + Write-Host " docker run -d --name $ContainerName -e PDCP_API_KEY=your-key -e PDCP_TEAM_ID=your-id $ImageName" -ForegroundColor Gray + exit 1 +} + +Write-Host "" +Write-Host "=== Deployment completed successfully ===" -ForegroundColor Green +Write-Host "" +Write-Host "Container Name: $ContainerName" -ForegroundColor Cyan +Write-Host "Image: $ImageName" -ForegroundColor Cyan +Write-Host "Container ID: $newContainerId" -ForegroundColor Cyan +Write-Host "" +Write-Host "Useful commands:" -ForegroundColor Yellow +Write-Host " docker logs $ContainerName -f # View logs" -ForegroundColor Gray +Write-Host " docker stop $ContainerName # Stop container" -ForegroundColor Gray +Write-Host " docker restart $ContainerName # Restart container" -ForegroundColor Gray +Write-Host " docker ps # List running containers" -ForegroundColor Gray + diff --git a/setup.go.ps1 b/setup.go.ps1 new file mode 100644 index 0000000..4faf335 --- /dev/null +++ b/setup.go.ps1 @@ -0,0 +1,427 @@ +#Requires -RunAsAdministrator + +<# +.SYNOPSIS + Automated silent installation and update of Go (Golang) for Windows + +.DESCRIPTION + This script downloads and installs Go (Golang) for Windows silently. + It checks if Go is already installed and updates it if a newer version is available. +#> + +$ErrorActionPreference = "Stop" + +# Check if running as Administrator +$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $isAdmin) { + Write-Error "This script must be run as Administrator. Please run PowerShell as Administrator and try again." + exit 1 +} + +# Function to check if Go is already installed +function Test-GoInstalled { + try { + $goVersion = go version 2>$null + if ($goVersion) { + return @{ Installed = $true; Version = $goVersion } + } + } catch { + # Go not found + } + + # Check for Go installation directory + $goPaths = @( + "${env:ProgramFiles}\Go", + "${env:ProgramFiles(x86)}\Go", + "$env:LOCALAPPDATA\Go" + ) + + foreach ($path in $goPaths) { + if (Test-Path $path) { + $goExe = Join-Path $path "bin\go.exe" + if (Test-Path $goExe) { + return @{ Installed = $true; Version = "Unknown (installation found)" } + } + } + } + + return @{ Installed = $false; Version = $null } +} + +# Function to get installed Go version number +function Get-InstalledGoVersion { + try { + $versionOutput = go version 2>$null + if ($versionOutput -match 'go(\d+\.\d+(?:\.\d+)?)') { + return $matches[1] + } + # Try alternative format + if ($versionOutput -match '(\d+\.\d+(?:\.\d+)?)') { + return $matches[1] + } + } catch { + # Version parsing failed + } + + # Try to get version from Go installation + $goPaths = @( + "${env:ProgramFiles}\Go", + "${env:ProgramFiles(x86)}\Go", + "$env:LOCALAPPDATA\Go" + ) + + foreach ($path in $goPaths) { + $goExe = Join-Path $path "bin\go.exe" + if (Test-Path $goExe) { + try { + $fileVersion = (Get-Item $goExe).VersionInfo.FileVersion + if ($fileVersion) { + return $fileVersion + } + } catch { + # Version info not available + } + } + } + + return $null +} + +# Function to get latest Go version from golang.org +function Get-LatestGoVersion { + try { + $ProgressPreference = 'SilentlyContinue' + # Use golang.org/dl API to get latest stable version + $releasesUrl = "https://go.dev/dl/?mode=json" + $response = Invoke-RestMethod -Uri $releasesUrl -UseBasicParsing -ErrorAction Stop + + if ($response -and $response.Count -gt 0) { + # Filter for stable releases (not beta/rc) and Windows amd64 + $stableReleases = $response | Where-Object { + $_.version -notmatch 'beta|rc' -and + ($_.files | Where-Object { $_.os -eq 'windows' -and $_.arch -eq 'amd64' }) + } | Sort-Object -Property @{Expression={[Version]($_.version -replace '^go','')}} -Descending + + if ($stableReleases -and $stableReleases.Count -gt 0) { + $latest = $stableReleases[0] + $version = $latest.version -replace '^go', '' + return $version + } + } + } catch { + Write-Warning "Could not fetch latest version from golang.org API: $_" + } + + # Fallback: try to get from GitHub releases + try { + $ProgressPreference = 'SilentlyContinue' + $releasesUrl = "https://api.github.com/repos/golang/go/releases/latest" + $response = Invoke-RestMethod -Uri $releasesUrl -UseBasicParsing -ErrorAction Stop + + if ($response.tag_name) { + # Remove 'go' prefix if present + $version = $response.tag_name -replace '^go', '' + return $version + } + } catch { + Write-Warning "Could not fetch latest version from GitHub API: $_" + } + + # Fallback: return null to indicate we should proceed with installation/update + return $null +} + +# Function to compare version numbers +function Compare-Version { + param( + [string]$Version1, + [string]$Version2 + ) + + if ([string]::IsNullOrEmpty($Version1) -or [string]::IsNullOrEmpty($Version2)) { + return $null + } + + try { + $v1 = [Version]$Version1 + $v2 = [Version]$Version2 + + if ($v1 -gt $v2) { + return 1 + } elseif ($v1 -lt $v2) { + return -1 + } else { + return 0 + } + } catch { + Write-Warning "Version comparison failed: $_" + return $null + } +} + +# Function to find Go installation directory +function Get-GoInstallPath { + $goPaths = @( + "${env:ProgramFiles}\Go", + "${env:ProgramFiles(x86)}\Go", + "$env:LOCALAPPDATA\Go" + ) + + foreach ($path in $goPaths) { + if (Test-Path $path) { + $goExe = Join-Path $path "bin\go.exe" + if (Test-Path $goExe) { + return $path + } + } + } + + return $null +} + +# Function to uninstall Go +function Uninstall-Go { + Write-Host "Uninstalling existing Go installation..." -ForegroundColor Cyan + + $goPath = Get-GoInstallPath + if ($goPath) { + Write-Host "Found Go installation at: $goPath" -ForegroundColor Cyan + + # Try to find uninstaller + $uninstallPaths = @( + (Join-Path $goPath "Uninstall.exe"), + (Join-Path (Split-Path $goPath) "Go\Uninstall.exe") + ) + + $uninstallerFound = $false + foreach ($uninstallPath in $uninstallPaths) { + if (Test-Path $uninstallPath) { + Write-Host "Found uninstaller at: $uninstallPath" -ForegroundColor Cyan + try { + $process = Start-Process -FilePath $uninstallPath -ArgumentList @("/S") -Wait -PassThru -NoNewWindow -ErrorAction Stop + + if ($process.ExitCode -eq 0) { + Write-Host "Go uninstalled successfully." -ForegroundColor Green + $uninstallerFound = $true + Start-Sleep -Seconds 3 + break + } + } catch { + Write-Warning "Error running uninstaller: $_" + } + } + } + + if (-not $uninstallerFound) { + # Try to remove via MSI if it was installed via MSI + try { + $product = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name -like "*Go*" } | Select-Object -First 1 + if ($product) { + Write-Host "Uninstalling Go via MSI..." -ForegroundColor Cyan + $product.Uninstall() | Out-Null + Start-Sleep -Seconds 3 + $uninstallerFound = $true + } + } catch { + Write-Warning "Could not uninstall via MSI: $_" + } + } + + if (-not $uninstallerFound) { + Write-Warning "Could not find Go uninstaller. The new installation will overwrite the existing one." + } + } else { + Write-Warning "Could not find Go installation directory." + } +} + +# Function to update PATH environment variable +function Update-GoPath { + $goPath = Get-GoInstallPath + if (-not $goPath) { + return + } + + $goBinPath = Join-Path $goPath "bin" + $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") + + if ($currentPath -notlike "*$goBinPath*") { + Write-Host "Adding Go to system PATH..." -ForegroundColor Cyan + $newPath = $currentPath + ";$goBinPath" + [Environment]::SetEnvironmentVariable("Path", $newPath, "Machine") + $env:Path += ";$goBinPath" + Write-Host "Go bin directory added to PATH." -ForegroundColor Green + } else { + Write-Host "Go is already in PATH." -ForegroundColor Green + } +} + +# Function to test if Go is accessible +function Test-GoRunning { + try { + $null = go version 2>$null + return ($LASTEXITCODE -eq 0) + } catch { + return $false + } +} + +# Check if Go is already installed +$goStatus = Test-GoInstalled +$needsUpdate = $false + +if ($goStatus.Installed) { + Write-Host "Go is already installed: $($goStatus.Version)" -ForegroundColor Green + + # Check if update is needed + Write-Host "Checking for updates..." -ForegroundColor Cyan + $installedVersion = Get-InstalledGoVersion + $latestVersion = Get-LatestGoVersion + + if ($installedVersion -and $latestVersion) { + $comparison = Compare-Version -Version1 $latestVersion -Version2 $installedVersion + if ($comparison -eq 1) { + Write-Host "Newer version available: $latestVersion (current: $installedVersion)" -ForegroundColor Yellow + $needsUpdate = $true + } elseif ($comparison -eq 0) { + Write-Host "Go is up to date (version $installedVersion)." -ForegroundColor Green + # Ensure PATH is set correctly + Update-GoPath + exit 0 + } else { + Write-Host "Installed version ($installedVersion) is newer than latest available ($latestVersion)." -ForegroundColor Green + exit 0 + } + } elseif (-not $latestVersion) { + Write-Host "Could not determine latest version. Skipping update check." -ForegroundColor Yellow + Update-GoPath + exit 0 + } else { + Write-Host "Could not determine installed version. Proceeding with update..." -ForegroundColor Yellow + $needsUpdate = $true + } + + if ($needsUpdate) { + Write-Host "`nUpdating Go..." -ForegroundColor Cyan + Uninstall-Go + } +} else { + Write-Host "Go is not installed. Starting installation..." -ForegroundColor Cyan +} + +# Create temporary directory for download +$tempDir = Join-Path $env:TEMP "go-install" +if (-not (Test-Path $tempDir)) { + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null +} + +# Get latest version if not already determined +if (-not $latestVersion) { + $latestVersion = Get-LatestGoVersion +} + +if (-not $latestVersion) { + Write-Error "Could not determine latest Go version. Please check your internet connection and try again." + exit 1 +} + +# Go MSI installer download URL +$goInstallerUrl = "https://go.dev/dl/go$latestVersion.windows-amd64.msi" +$goInstallerPath = Join-Path $tempDir "go-installer.msi" + +try { + # Download Go installer + $action = if ($needsUpdate) { "update" } else { "installation" } + Write-Host "Downloading Go installer for $action..." -ForegroundColor Cyan + Write-Host "Version: $latestVersion" -ForegroundColor Gray + Write-Host "Source: $goInstallerUrl" -ForegroundColor Gray + + try { + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri $goInstallerUrl -OutFile $goInstallerPath -UseBasicParsing -ErrorAction Stop + } catch { + throw "Failed to download Go installer from $goInstallerUrl : $_" + } + + if (-not (Test-Path $goInstallerPath)) { + throw "Downloaded file not found at expected location: $goInstallerPath" + } + + $fileSize = (Get-Item $goInstallerPath).Length / 1MB + Write-Host "Downloaded installer size: $([math]::Round($fileSize, 2)) MB" -ForegroundColor Gray + + Write-Host "Download completed. Starting silent $action..." -ForegroundColor Cyan + Write-Host "This may take a few minutes. Please wait..." -ForegroundColor Yellow + + # Install Go silently using msiexec + # /i = Install + # /quiet = Silent installation + # /norestart = Don't restart + # /qn = No UI + $installArgs = @( + "/i", + "`"$goInstallerPath`"", + "/quiet", + "/norestart", + "/qn" + ) + + $process = Start-Process -FilePath "msiexec.exe" -ArgumentList $installArgs -Wait -PassThru -NoNewWindow + + if ($process.ExitCode -eq 0 -or $process.ExitCode -eq 3010) { + # Exit code 0 = success, 3010 = success but requires reboot + if ($needsUpdate) { + Write-Host "Go update completed successfully!" -ForegroundColor Green + } else { + Write-Host "Go installation completed successfully!" -ForegroundColor Green + } + + if ($process.ExitCode -eq 3010) { + Write-Host "A system reboot is required to complete the $action." -ForegroundColor Yellow + Write-Host "Please restart your computer and Go will be ready to use." -ForegroundColor Yellow + } else { + $actionPast = if ($needsUpdate) { "updated" } else { "installed" } + Write-Host "Go has been $actionPast." -ForegroundColor Green + + # Update PATH + Update-GoPath + + # Refresh environment variables in current session + $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + + # Wait a moment for installation to complete + Write-Host "Waiting for installation to finalize..." -ForegroundColor Cyan + Start-Sleep -Seconds 5 + + # Verify Go is accessible + if (Test-GoRunning) { + $finalVersion = go version 2>$null + if ($finalVersion) { + Write-Host "Go is installed and accessible: $finalVersion" -ForegroundColor Green + } + } else { + Write-Host "Go has been installed, but you may need to:" -ForegroundColor Yellow + Write-Host " 1. Restart your PowerShell session" -ForegroundColor Yellow + Write-Host " 2. Or restart your computer" -ForegroundColor Yellow + Write-Host " 3. Verify PATH includes: $(Get-GoInstallPath)\bin" -ForegroundColor Yellow + } + } + } else { + throw "Go $action failed with exit code: $($process.ExitCode)" + } + +} catch { + Write-Error "Error during Go $action : $_" + exit 1 +} finally { + # Clean up installer file + if (Test-Path $goInstallerPath) { + Remove-Item -Path $goInstallerPath -Force -ErrorAction SilentlyContinue + } + if (Test-Path $tempDir) { + Remove-Item -Path $tempDir -Force -ErrorAction SilentlyContinue + } +} + +Write-Host "`nScript completed successfully!" -ForegroundColor Green + diff --git a/setup.windows.desktop.ps1 b/setup.windows.desktop.ps1 new file mode 100644 index 0000000..9884cf1 --- /dev/null +++ b/setup.windows.desktop.ps1 @@ -0,0 +1,356 @@ +#Requires -RunAsAdministrator + +<# +.SYNOPSIS + Automated silent installation and update of Docker Desktop for Windows + +.DESCRIPTION + This script downloads and installs Docker Desktop for Windows silently. + It checks if Docker is already installed and updates it if a newer version is available. +#> + +$ErrorActionPreference = "Stop" + +# Check if running as Administrator +$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $isAdmin) { + Write-Error "This script must be run as Administrator. Please run PowerShell as Administrator and try again." + exit 1 +} + +# Function to check if Docker is already installed +function Test-DockerInstalled { + try { + $dockerVersion = docker --version 2>$null + if ($dockerVersion) { + return @{ Installed = $true; Version = $dockerVersion } + } + } catch { + # Docker not found + } + + # Check for Docker Desktop service + $dockerService = Get-Service -Name "com.docker.service" -ErrorAction SilentlyContinue + if ($dockerService) { + return @{ Installed = $true; Version = "Unknown (service exists)" } + } + + # Check for Docker Desktop executable + $dockerDesktopPath = "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe" + if (Test-Path $dockerDesktopPath) { + return @{ Installed = $true; Version = "Unknown (executable exists)" } + } + + # Check for Docker service (for Docker CE installations) + $dockerServiceCE = Get-Service -Name "docker" -ErrorAction SilentlyContinue + if ($dockerServiceCE) { + return @{ Installed = $true; Version = "Docker CE (service exists)" } + } + + return @{ Installed = $false; Version = $null } +} + +# Function to wait for Docker daemon to be ready +function Wait-Docker { + Write-Host "Waiting for Docker daemon to be ready..." -ForegroundColor Cyan + $dockerReady = $false + $startTime = Get-Date + $maxWaitMinutes = 2 + + while (-not $dockerReady) { + try { + $null = docker version 2>$null + if ($LASTEXITCODE -eq 0) { + $dockerReady = $true + Write-Host "Docker daemon is ready." -ForegroundColor Green + return $true + } + } catch { + # Docker not ready yet + } + + $timeElapsed = $(Get-Date) - $startTime + if ($timeElapsed.TotalMinutes -ge $maxWaitMinutes) { + Write-Warning "Docker daemon did not become ready within $maxWaitMinutes minutes." + return $false + } + + Start-Sleep -Seconds 2 + } + + return $false +} + +# Function to test if Docker is running and accessible +function Test-DockerRunning { + try { + $null = docker version 2>$null + return ($LASTEXITCODE -eq 0) + } catch { + return $false + } +} + +# Function to get installed Docker version number +function Get-InstalledDockerVersion { + try { + $versionOutput = docker --version 2>$null + if ($versionOutput -match 'version (\d+\.\d+\.\d+)') { + return $matches[1] + } + # Try alternative format + if ($versionOutput -match '(\d+\.\d+\.\d+)') { + return $matches[1] + } + } catch { + # Version parsing failed + } + + # Try to get version from Docker Desktop installation + $dockerDesktopPath = "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe" + if (Test-Path $dockerDesktopPath) { + $fileVersion = (Get-Item $dockerDesktopPath).VersionInfo.FileVersion + if ($fileVersion) { + return $fileVersion + } + } + + return $null +} + +# Function to get latest Docker Desktop version from GitHub releases +function Get-LatestDockerVersion { + try { + $ProgressPreference = 'SilentlyContinue' + $releasesUrl = "https://api.github.com/repos/docker/docker-desktop/releases/latest" + $response = Invoke-RestMethod -Uri $releasesUrl -UseBasicParsing -ErrorAction Stop + + if ($response.tag_name) { + # Remove 'v' prefix if present + $version = $response.tag_name -replace '^v', '' + return $version + } + } catch { + Write-Warning "Could not fetch latest version from GitHub API: $_" + } + + # Fallback: return null to indicate we should proceed with installation/update + return $null +} + +# Function to compare version numbers +function Compare-Version { + param( + [string]$Version1, + [string]$Version2 + ) + + if ([string]::IsNullOrEmpty($Version1) -or [string]::IsNullOrEmpty($Version2)) { + return $null + } + + try { + $v1 = [Version]$Version1 + $v2 = [Version]$Version2 + + if ($v1 -gt $v2) { + return 1 + } elseif ($v1 -lt $v2) { + return -1 + } else { + return 0 + } + } catch { + Write-Warning "Version comparison failed: $_" + return $null + } +} + +# Function to uninstall Docker Desktop +function Uninstall-DockerDesktop { + Write-Host "Uninstalling existing Docker Desktop..." -ForegroundColor Cyan + + # Stop Docker Desktop service if running + try { + $dockerService = Get-Service -Name "com.docker.service" -ErrorAction SilentlyContinue + if ($dockerService -and $dockerService.Status -eq 'Running') { + Write-Host "Stopping Docker Desktop service..." -ForegroundColor Cyan + Stop-Service -Name "com.docker.service" -Force -ErrorAction SilentlyContinue + Start-Sleep -Seconds 3 + } + } catch { + Write-Warning "Could not stop Docker Desktop service: $_" + } + + # Try to find Docker Desktop uninstaller + $uninstallPaths = @( + "${env:ProgramFiles}\Docker\Docker\uninstall.exe", + "${env:ProgramFiles(x86)}\Docker\Docker\uninstall.exe", + "${env:LocalAppData}\Programs\Docker\Docker\uninstall.exe" + ) + + $uninstallerFound = $false + foreach ($path in $uninstallPaths) { + if (Test-Path $path) { + Write-Host "Found uninstaller at: $path" -ForegroundColor Cyan + try { + $process = Start-Process -FilePath $path -ArgumentList @("--quiet", "--unattended") -Wait -PassThru -NoNewWindow -ErrorAction Stop + + if ($process.ExitCode -eq 0 -or $process.ExitCode -eq 3010) { + Write-Host "Docker Desktop uninstalled successfully." -ForegroundColor Green + $uninstallerFound = $true + + # Wait a bit for cleanup + Write-Host "Waiting for cleanup to complete..." -ForegroundColor Cyan + Start-Sleep -Seconds 5 + break + } else { + Write-Warning "Uninstaller exited with code: $($process.ExitCode)" + } + } catch { + Write-Warning "Error running uninstaller at $path : $_" + } + } + } + + if (-not $uninstallerFound) { + Write-Warning "Could not find Docker Desktop uninstaller. Proceeding with installation anyway..." + Write-Warning "The new installation may overwrite the existing installation." + } +} + +# Check if Docker is already installed +$dockerStatus = Test-DockerInstalled +$needsUpdate = $false + +if ($dockerStatus.Installed) { + Write-Host "Docker is already installed: $($dockerStatus.Version)" -ForegroundColor Green + + # Check if update is needed + Write-Host "Checking for updates..." -ForegroundColor Cyan + $installedVersion = Get-InstalledDockerVersion + $latestVersion = Get-LatestDockerVersion + + if ($installedVersion -and $latestVersion) { + $comparison = Compare-Version -Version1 $latestVersion -Version2 $installedVersion + if ($comparison -eq 1) { + Write-Host "Newer version available: $latestVersion (current: $installedVersion)" -ForegroundColor Yellow + $needsUpdate = $true + } elseif ($comparison -eq 0) { + Write-Host "Docker is up to date (version $installedVersion)." -ForegroundColor Green + exit 0 + } else { + Write-Host "Installed version ($installedVersion) is newer than latest available ($latestVersion)." -ForegroundColor Green + exit 0 + } + } elseif (-not $latestVersion) { + Write-Host "Could not determine latest version. Skipping update check." -ForegroundColor Yellow + exit 0 + } else { + Write-Host "Could not determine installed version. Proceeding with update..." -ForegroundColor Yellow + $needsUpdate = $true + } + + if ($needsUpdate) { + Write-Host "`nUpdating Docker Desktop..." -ForegroundColor Cyan + Uninstall-DockerDesktop + } +} else { + Write-Host "Docker is not installed. Starting installation..." -ForegroundColor Cyan +} + +# Create temporary directory for download +$tempDir = Join-Path $env:TEMP "docker-install" +if (-not (Test-Path $tempDir)) { + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null +} + +# Docker Desktop download URL (latest stable version) +$dockerInstallerUrl = "https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe" +$dockerInstallerPath = Join-Path $tempDir "DockerDesktopInstaller.exe" + +try { + # Download Docker Desktop installer + $action = if ($needsUpdate) { "update" } else { "installation" } + Write-Host "Downloading Docker Desktop installer for $action..." -ForegroundColor Cyan + Write-Host "Source: $dockerInstallerUrl" -ForegroundColor Gray + + try { + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri $dockerInstallerUrl -OutFile $dockerInstallerPath -UseBasicParsing -ErrorAction Stop + } catch { + throw "Failed to download Docker Desktop installer from $dockerInstallerUrl : $_" + } + + if (-not (Test-Path $dockerInstallerPath)) { + throw "Downloaded file not found at expected location: $dockerInstallerPath" + } + + $fileSize = (Get-Item $dockerInstallerPath).Length / 1MB + Write-Host "Downloaded installer size: $([math]::Round($fileSize, 2)) MB" -ForegroundColor Gray + + Write-Host "Download completed. Starting silent $action..." -ForegroundColor Cyan + Write-Host "This may take several minutes. Please wait..." -ForegroundColor Yellow + + # Install Docker Desktop silently + # install = Install/update mode + # --quiet = Quiet mode (no UI) + # --accept-license = Accept license agreement + $installArgs = @( + "install", + "--quiet", + "--accept-license" + ) + + $process = Start-Process -FilePath $dockerInstallerPath -ArgumentList $installArgs -Wait -PassThru -NoNewWindow + + if ($process.ExitCode -eq 0 -or $process.ExitCode -eq 3010) { + # Exit code 0 = success, 3010 = success but requires reboot + if ($needsUpdate) { + Write-Host "Docker Desktop update completed successfully!" -ForegroundColor Green + } else { + Write-Host "Docker Desktop installation completed successfully!" -ForegroundColor Green + } + + if ($process.ExitCode -eq 3010) { + Write-Host "A system reboot is required to complete the $action." -ForegroundColor Yellow + Write-Host "Please restart your computer and Docker Desktop will be ready to use." -ForegroundColor Yellow + } else { + $actionPast = if ($needsUpdate) { "updated" } else { "installed" } + Write-Host "Docker Desktop has been $actionPast." -ForegroundColor Green + + # Wait a moment for services to start + Write-Host "Waiting for Docker services to initialize..." -ForegroundColor Cyan + Start-Sleep -Seconds 10 + + # Verify Docker is accessible (if not requiring reboot) + if (Test-DockerRunning) { + $finalVersion = docker --version 2>$null + if ($finalVersion) { + Write-Host "Docker is running: $finalVersion" -ForegroundColor Green + } + } else { + Write-Host "Docker Desktop may need a few more moments to start, or you may need to restart your computer." -ForegroundColor Yellow + } + + Write-Host "After restart (if needed), Docker Desktop should start automatically." -ForegroundColor Yellow + } + } else { + throw "Docker Desktop $action failed with exit code: $($process.ExitCode)" + } + +} catch { + Write-Error "Error during Docker Desktop $action : $_" + exit 1 +} finally { + # Clean up installer file + if (Test-Path $dockerInstallerPath) { + Remove-Item -Path $dockerInstallerPath -Force -ErrorAction SilentlyContinue + } + if (Test-Path $tempDir) { + Remove-Item -Path $tempDir -Force -ErrorAction SilentlyContinue + } +} + +Write-Host "`nScript completed successfully!" -ForegroundColor Green + diff --git a/setup.windows.server.ps1 b/setup.windows.server.ps1 new file mode 100644 index 0000000..aec1783 --- /dev/null +++ b/setup.windows.server.ps1 @@ -0,0 +1,561 @@ +#Requires -RunAsAdministrator + +<# +.SYNOPSIS + Automated silent installation and update of Docker Engine from binaries for Windows Server + +.DESCRIPTION + This script downloads and installs Docker Engine from static binaries for Windows Server. + It follows the official Docker documentation for binary installation: + https://docs.docker.com/engine/install/binaries/ + + Note: This installs Docker for Windows containers only (not Linux containers). + For Windows 10/11, use setup.windows.desktop.ps1 instead. +#> + +$ErrorActionPreference = "Stop" + +# Check if running as Administrator +$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $isAdmin) { + Write-Error "This script must be run as Administrator. Please run PowerShell as Administrator and try again." + exit 1 +} + +# Docker installation paths +$dockerInstallPath = Join-Path $env:ProgramFiles "Docker" +$dockerExePath = Join-Path $dockerInstallPath "docker.exe" +$dockerdExePath = Join-Path $dockerInstallPath "dockerd.exe" + +# Global flag for restart requirement +$script:RebootRequired = $false + +# Check if this is a resume after restart (optional marker file) +$resumeMarker = Join-Path $env:TEMP "docker-setup-resume.txt" + +# Function to check if Windows Containers feature is installed +function Test-ContainersFeature { + try { + if (Get-Command Get-WindowsFeature -ErrorAction SilentlyContinue) { + # Windows Server with Server Manager + $feature = Get-WindowsFeature -Name Containers -ErrorAction SilentlyContinue + if ($feature) { + return $feature.Installed + } + } else { + # Windows Server Core or newer versions + $feature = Get-WindowsOptionalFeature -Online -FeatureName Containers -ErrorAction SilentlyContinue + if ($feature) { + return ($feature.State -eq "Enabled") + } + } + } catch { + Write-Warning "Could not check Containers feature status: $_" + } + return $false +} + +# Function to install Windows Containers feature +function Install-ContainersFeature { + Write-Host "Checking Windows Containers feature..." -ForegroundColor Cyan + + if (Test-ContainersFeature) { + Write-Host "Windows Containers feature is already installed." -ForegroundColor Green + return + } + + Write-Host "Installing Windows Containers feature..." -ForegroundColor Cyan + Write-Host "This may take several minutes and may require a system restart." -ForegroundColor Yellow + + try { + if (Get-Command Install-WindowsFeature -ErrorAction SilentlyContinue) { + # Windows Server with Server Manager + $result = Install-WindowsFeature -Name Containers -IncludeManagementTools + + if ($result.RestartNeeded -eq "Yes") { + $script:RebootRequired = $true + Write-Host "A system restart is required to complete the Containers feature installation." -ForegroundColor Yellow + } + + if ($result.Success) { + Write-Host "Windows Containers feature installed successfully." -ForegroundColor Green + } else { + throw "Failed to install Containers feature" + } + } else { + # Windows Server Core or newer versions + $result = Enable-WindowsOptionalFeature -Online -FeatureName Containers -All -NoRestart + + if ($result.RestartNeeded -eq $true) { + $script:RebootRequired = $true + Write-Host "A system restart is required to complete the Containers feature installation." -ForegroundColor Yellow + } + + Write-Host "Windows Containers feature installed successfully." -ForegroundColor Green + } + } catch { + Write-Error "Failed to install Containers feature: $_" + throw + } +} + +# Function to check if restart is required and handle it +function Test-AndHandleRestart { + param( + [switch]$Silent + ) + + if ($script:RebootRequired) { + Write-Host "" + Write-Host "=== RESTART REQUIRED ===" -ForegroundColor Yellow + Write-Host "A system restart is required to complete the installation." -ForegroundColor Yellow + Write-Host "Please restart your computer and then run this script again to continue." -ForegroundColor Yellow + Write-Host "" + + # Create resume marker + "Resume after restart" | Out-File -FilePath $resumeMarker -Force + + if (-not $Silent) { + $restart = Read-Host "Would you like to restart now? (Y/N)" + if ($restart -eq "Y" -or $restart -eq "y") { + Write-Host "Restarting computer in 10 seconds..." -ForegroundColor Cyan + Write-Host "Press Ctrl+C to cancel" -ForegroundColor Gray + Start-Sleep -Seconds 10 + Restart-Computer -Force + } else { + Write-Host "Please restart your computer manually and run this script again." -ForegroundColor Yellow + Write-Host "The script will automatically resume after restart." -ForegroundColor Gray + exit 0 + } + } else { + Write-Host "Please restart your computer manually and run this script again." -ForegroundColor Yellow + Write-Host "The script will automatically resume after restart." -ForegroundColor Gray + exit 0 + } + } +} + +# Function to check if Docker is already installed +function Test-DockerInstalled { + try { + $dockerVersion = docker --version 2>$null + if ($dockerVersion) { + return @{ Installed = $true; Version = $dockerVersion } + } + } catch { + # Docker not found + } + + # Check for Docker service + $dockerService = Get-Service -Name "docker" -ErrorAction SilentlyContinue + if ($dockerService) { + return @{ Installed = $true; Version = "Unknown (service exists)" } + } + + # Check for Docker binaries + if (Test-Path $dockerExePath) { + return @{ Installed = $true; Version = "Unknown (binary exists)" } + } + + return @{ Installed = $false; Version = $null } +} + +# Function to wait for Docker daemon to be ready +function Wait-Docker { + Write-Host "Waiting for Docker daemon to be ready..." -ForegroundColor Cyan + $dockerReady = $false + $startTime = Get-Date + $maxWaitMinutes = 2 + + while (-not $dockerReady) { + try { + $null = docker version 2>$null + if ($LASTEXITCODE -eq 0) { + $dockerReady = $true + Write-Host "Docker daemon is ready." -ForegroundColor Green + return $true + } + } catch { + # Docker not ready yet + } + + $timeElapsed = $(Get-Date) - $startTime + if ($timeElapsed.TotalMinutes -ge $maxWaitMinutes) { + Write-Warning "Docker daemon did not become ready within $maxWaitMinutes minutes." + return $false + } + + Start-Sleep -Seconds 2 + } + + return $false +} + +# Function to test if Docker is running and accessible +function Test-DockerRunning { + try { + $null = docker version 2>$null + return ($LASTEXITCODE -eq 0) + } catch { + return $false + } +} + +# Function to get installed Docker version number +function Get-InstalledDockerVersion { + try { + $versionOutput = docker --version 2>$null + if ($versionOutput -match 'version (\d+\.\d+\.\d+)') { + return $matches[1] + } + # Try alternative format + if ($versionOutput -match '(\d+\.\d+\.\d+)') { + return $matches[1] + } + } catch { + # Version parsing failed + } + + return $null +} + +# Function to get latest Docker version from download page +function Get-LatestDockerVersion { + try { + $ProgressPreference = 'SilentlyContinue' + $downloadUrl = "https://download.docker.com/win/static/stable/x86_64/" + $response = Invoke-WebRequest -Uri $downloadUrl -UseBasicParsing -ErrorAction Stop + + # Parse HTML to find latest version + # Look for links like "docker-24.0.0.zip" + $matches = [regex]::Matches($response.Content, 'docker-(\d+\.\d+\.\d+)\.zip') + if ($matches.Count -gt 0) { + $versions = $matches | ForEach-Object { [Version]$_.Groups[1].Value } | Sort-Object -Descending + if ($versions.Count -gt 0) { + return $versions[0].ToString() + } + } + } catch { + Write-Warning "Could not fetch latest version from download page: $_" + } + + # Fallback: return null to indicate we should proceed with installation/update + return $null +} + +# Function to compare version numbers +function Compare-Version { + param( + [string]$Version1, + [string]$Version2 + ) + + if ([string]::IsNullOrEmpty($Version1) -or [string]::IsNullOrEmpty($Version2)) { + return $null + } + + try { + $v1 = [Version]$Version1 + $v2 = [Version]$Version2 + + if ($v1 -gt $v2) { + return 1 + } elseif ($v1 -lt $v2) { + return -1 + } else { + return 0 + } + } catch { + Write-Warning "Version comparison failed: $_" + return $null + } +} + +# Function to stop and unregister Docker service +function Stop-DockerService { + Write-Host "Stopping Docker service..." -ForegroundColor Cyan + + try { + $dockerService = Get-Service -Name "docker" -ErrorAction SilentlyContinue + if ($dockerService -and $dockerService.Status -eq 'Running') { + Stop-Service -Name "docker" -Force -ErrorAction Stop + Write-Host "Docker service stopped." -ForegroundColor Green + Start-Sleep -Seconds 2 + } + } catch { + Write-Warning "Could not stop Docker service: $_" + } + + # Unregister service if it exists + try { + $service = Get-Service -Name "docker" -ErrorAction SilentlyContinue + if ($service) { + Write-Host "Unregistering Docker service..." -ForegroundColor Cyan + & $dockerdExePath --unregister-service 2>$null + Start-Sleep -Seconds 2 + } + } catch { + Write-Warning "Could not unregister Docker service: $_" + } +} + +# Function to update PATH environment variable +function Update-DockerPath { + if (-not (Test-Path $dockerInstallPath)) { + Write-Warning "Docker installation path not found: $dockerInstallPath" + return + } + + $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") + + if ($currentPath -notlike "*$dockerInstallPath*") { + Write-Host "Adding Docker to system PATH..." -ForegroundColor Cyan + $newPath = $currentPath + ";$dockerInstallPath" + [Environment]::SetEnvironmentVariable("Path", $newPath, "Machine") + + # Also update current session PATH + $env:Path += ";$dockerInstallPath" + + Write-Host "Docker directory added to PATH: $dockerInstallPath" -ForegroundColor Green + } else { + Write-Host "Docker is already in system PATH." -ForegroundColor Green + } +} + +# Check if resuming after restart +if (Test-Path $resumeMarker) { + Write-Host "Resuming installation after restart..." -ForegroundColor Cyan + Remove-Item -Path $resumeMarker -Force -ErrorAction SilentlyContinue + Write-Host "" +} + +# Install Windows Containers feature +Write-Host "=== Installing Windows Containers Feature ===" -ForegroundColor Cyan +Write-Host "" +try { + Install-ContainersFeature + Test-AndHandleRestart -Silent +} catch { + Write-Error "Failed to install Windows Containers feature: $_" + exit 1 +} +Write-Host "" + +# Check if Docker is already installed +Write-Host "=== Checking Docker Installation ===" -ForegroundColor Cyan +Write-Host "" + +$dockerStatus = Test-DockerInstalled +$needsUpdate = $false + +if ($dockerStatus.Installed) { + Write-Host "Docker is already installed: $($dockerStatus.Version)" -ForegroundColor Green + + # Check if update is needed + Write-Host "Checking for updates..." -ForegroundColor Cyan + $installedVersion = Get-InstalledDockerVersion + $latestVersion = Get-LatestDockerVersion + + if ($installedVersion -and $latestVersion) { + $comparison = Compare-Version -Version1 $latestVersion -Version2 $installedVersion + if ($comparison -eq 1) { + Write-Host "Newer version available: $latestVersion (current: $installedVersion)" -ForegroundColor Yellow + $needsUpdate = $true + } elseif ($comparison -eq 0) { + Write-Host "Docker is up to date (version $installedVersion)." -ForegroundColor Green + exit 0 + } else { + Write-Host "Installed version ($installedVersion) is newer than latest available ($latestVersion)." -ForegroundColor Green + exit 0 + } + } elseif (-not $latestVersion) { + Write-Host "Could not determine latest version. Skipping update check." -ForegroundColor Yellow + exit 0 + } else { + Write-Host "Could not determine installed version. Proceeding with update..." -ForegroundColor Yellow + $needsUpdate = $true + } + + if ($needsUpdate) { + Write-Host "`nUpdating Docker..." -ForegroundColor Cyan + Stop-DockerService + } +} else { + Write-Host "Docker is not installed. Starting installation..." -ForegroundColor Cyan +} + +Write-Host "" + +# Download and install Docker binaries +Write-Host "=== Installing Docker Binaries ===" -ForegroundColor Cyan +Write-Host "" + +# Get latest version if not already determined +if (-not $latestVersion) { + $latestVersion = Get-LatestDockerVersion +} + +if (-not $latestVersion) { + Write-Error "Could not determine latest Docker version. Please check your internet connection and try again." + exit 1 +} + +# Create temporary directory for download +$tempDir = Join-Path $env:TEMP "docker-install" +if (-not (Test-Path $tempDir)) { + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null +} + +# Docker binary download URL +$dockerZipUrl = "https://download.docker.com/win/static/stable/x86_64/docker-$latestVersion.zip" +$dockerZipPath = Join-Path $tempDir "docker-$latestVersion.zip" + +try { + # Download Docker binaries + $action = if ($needsUpdate) { "update" } else { "installation" } + Write-Host "Downloading Docker binaries for $action..." -ForegroundColor Cyan + Write-Host "Version: $latestVersion" -ForegroundColor Gray + Write-Host "Source: $dockerZipUrl" -ForegroundColor Gray + + try { + $ProgressPreference = 'SilentlyContinue' + Invoke-WebRequest -Uri $dockerZipUrl -OutFile $dockerZipPath -UseBasicParsing -ErrorAction Stop + } catch { + throw "Failed to download Docker binaries from $dockerZipUrl : $_" + } + + if (-not (Test-Path $dockerZipPath)) { + throw "Downloaded file not found at expected location: $dockerZipPath" + } + + $fileSize = (Get-Item $dockerZipPath).Length / 1MB + Write-Host "Downloaded archive size: $([math]::Round($fileSize, 2)) MB" -ForegroundColor Gray + + Write-Host "Download completed. Extracting binaries..." -ForegroundColor Cyan + + # Extract archive to Program Files + $extractPath = Join-Path $tempDir "docker-extract" + if (Test-Path $extractPath) { + Remove-Item -Path $extractPath -Recurse -Force -ErrorAction SilentlyContinue + } + New-Item -ItemType Directory -Path $extractPath -Force | Out-Null + + Expand-Archive -Path $dockerZipPath -DestinationPath $extractPath -Force -ErrorAction Stop + + # Find the docker directory in the extracted files + $dockerExtractedPath = Get-ChildItem -Path $extractPath -Directory | Where-Object { $_.Name -like "docker*" } | Select-Object -First 1 + if (-not $dockerExtractedPath) { + # Sometimes files are extracted directly without a subdirectory + $dockerExtractedPath = $extractPath + } + + Write-Host "Installing Docker binaries to $dockerInstallPath..." -ForegroundColor Cyan + + # Create installation directory if it doesn't exist + if (-not (Test-Path $dockerInstallPath)) { + New-Item -ItemType Directory -Path $dockerInstallPath -Force | Out-Null + } + + # Copy binaries + $binaries = @("docker.exe", "dockerd.exe") + foreach ($binary in $binaries) { + $sourcePath = Join-Path $dockerExtractedPath.FullName $binary + if (Test-Path $sourcePath) { + Copy-Item -Path $sourcePath -Destination (Join-Path $dockerInstallPath $binary) -Force -ErrorAction Stop + Write-Host "Installed $binary" -ForegroundColor Green + } else { + Write-Warning "Binary $binary not found in archive" + } + } + + # Update PATH environment variable + Update-DockerPath + + # Refresh PATH in current session + $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + + # Register Docker service + Write-Host "Registering Docker service..." -ForegroundColor Cyan + $registerResult = & $dockerdExePath --register-service 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Warning "Service registration output: $registerResult" + # Continue anyway, service might already be registered + } else { + Write-Host "Docker service registered successfully." -ForegroundColor Green + } + + # Start Docker service + Write-Host "Starting Docker service..." -ForegroundColor Cyan + try { + Start-Service -Name "docker" -ErrorAction Stop + Write-Host "Docker service started successfully." -ForegroundColor Green + } catch { + Write-Warning "Could not start Docker service: $_" + Write-Host "You may need to start it manually: Start-Service docker" -ForegroundColor Yellow + } + + # Wait a moment for service to start + Start-Sleep -Seconds 5 + + # Verify Docker is accessible + if (Test-DockerRunning) { + $finalVersion = docker --version 2>$null + if ($finalVersion) { + Write-Host "Docker is running: $finalVersion" -ForegroundColor Green + } + + # Test with hello-world (Windows container) + Write-Host "Testing Docker installation..." -ForegroundColor Cyan + try { + docker run --rm hello-world:nanoserver 2>&1 | Out-Null + if ($LASTEXITCODE -eq 0) { + Write-Host "Docker installation verified successfully!" -ForegroundColor Green + } + } catch { + Write-Warning "Could not run test container, but Docker appears to be installed." + } + } else { + Write-Host "Docker service may need a few more moments to start." -ForegroundColor Yellow + Write-Host "You can verify with: docker version" -ForegroundColor Yellow + } + + if ($needsUpdate) { + Write-Host "Docker update completed successfully!" -ForegroundColor Green + } else { + Write-Host "Docker installation completed successfully!" -ForegroundColor Green + } + + # Check if restart is still required after Docker installation + Test-AndHandleRestart -Silent + + # Remove resume marker if installation completed successfully + if (Test-Path $resumeMarker) { + Remove-Item -Path $resumeMarker -Force -ErrorAction SilentlyContinue + } + +} catch { + Write-Error "Error during Docker $action : $_" + exit 1 +} finally { + # Clean up temporary files + if (Test-Path $tempDir) { + Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } +} + +Write-Host "`nScript completed successfully!" -ForegroundColor Green +Write-Host "" +Write-Host "Docker binaries installed to: $dockerInstallPath" -ForegroundColor Cyan +Write-Host "Docker has been added to system PATH." -ForegroundColor Green +Write-Host "" +Write-Host "Note: If you're running this script in a new PowerShell session, you may need to:" -ForegroundColor Yellow +Write-Host " - Close and reopen your PowerShell window, OR" -ForegroundColor Gray +Write-Host " - Run: `$env:Path = [System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')" -ForegroundColor Gray +Write-Host "" +Write-Host "Useful commands:" -ForegroundColor Yellow +Write-Host " docker version # Check Docker version" -ForegroundColor Gray +Write-Host " docker run hello-world:nanoserver # Test with Windows container" -ForegroundColor Gray +Write-Host " Get-Service docker # Check service status" -ForegroundColor Gray +Write-Host " Start-Service docker # Start Docker service" -ForegroundColor Gray +Write-Host " Stop-Service docker # Stop Docker service" -ForegroundColor Gray +