<# .SYNOPSIS Install, upgrade, or uninstall Ollama on Windows. .DESCRIPTION Downloads and installs Ollama. Quick install: irm https://ollama.com/install.ps1 | iex Specific version: $env:OLLAMA_VERSION="0.5.7"; irm https://ollama.com/install.ps1 | iex Custom install directory: $env:OLLAMA_INSTALL_DIR="D:\Ollama"; irm https://ollama.com/install.ps1 | iex Uninstall: $env:OLLAMA_UNINSTALL=1; irm https://ollama.com/install.ps1 | iex Environment variables: OLLAMA_VERSION Target version (default: latest stable) OLLAMA_INSTALL_DIR Custom install directory OLLAMA_UNINSTALL Set to 1 to uninstall Ollama OLLAMA_DEBUG Enable verbose output .EXAMPLE irm https://ollama.com/install.ps1 | iex .EXAMPLE $env:OLLAMA_VERSION = "0.5.7"; irm https://ollama.com/install.ps1 | iex .LINK https://ollama.com #> $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" # -------------------------------------------------------------------------- # Configuration from environment variables # -------------------------------------------------------------------------- $Version = if ($env:OLLAMA_VERSION) { $env:OLLAMA_VERSION } else { "" } $InstallDir = if ($env:OLLAMA_INSTALL_DIR) { $env:OLLAMA_INSTALL_DIR } else { "" } $Uninstall = $env:OLLAMA_UNINSTALL -eq "1" $DebugInstall = [bool]$env:OLLAMA_DEBUG # -------------------------------------------------------------------------- # Constants # -------------------------------------------------------------------------- # OLLAMA_DOWNLOAD_URL for developer testing only $DownloadBaseURL = if ($env:OLLAMA_DOWNLOAD_URL) { $env:OLLAMA_DOWNLOAD_URL.TrimEnd('/') } else { "https://ollama.com/download" } $InnoSetupUninstallGuid = "{44E83376-CE68-45EB-8FC1-393500EB558C}_is1" # -------------------------------------------------------------------------- # Helpers # -------------------------------------------------------------------------- function Write-Status { param([string]$Message) if ($DebugInstall) { Write-Host $Message } } function Write-Step { param([string]$Message) if ($DebugInstall) { Write-Host ">>> $Message" -ForegroundColor Cyan } } function Test-Signature { param([string]$FilePath) $sig = Get-AuthenticodeSignature -FilePath $FilePath if ($sig.Status -ne "Valid") { Write-Status " Signature status: $($sig.Status)" return $false } # Verify it's signed by Ollama Inc. (check exact organization name) # Anchor with comma/boundary to prevent "O=Not Ollama Inc." from matching $subject = $sig.SignerCertificate.Subject if ($subject -notmatch "(^|, )O=Ollama Inc\.(,|$)") { Write-Status " Unexpected signer: $subject" return $false } Write-Status " Signature valid: $subject" return $true } function Find-InnoSetupInstall { # Check both HKCU (per-user) and HKLM (per-machine) locations $possibleKeys = @( "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$InnoSetupUninstallGuid", "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$InnoSetupUninstallGuid", "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$InnoSetupUninstallGuid" ) foreach ($key in $possibleKeys) { if (Test-Path $key) { Write-Status " Found install at: $key" return $key } } return $null } function Update-SessionPath { # Update PATH in current session so 'ollama' works immediately if ($InstallDir) { $ollamaDir = $InstallDir } else { $ollamaDir = Join-Path $env:LOCALAPPDATA "Programs\Ollama" } # Add to PATH if not already present if (Test-Path $ollamaDir) { $currentPath = $env:PATH -split ';' if ($ollamaDir -notin $currentPath) { $env:PATH = "$ollamaDir;$env:PATH" Write-Status " Added $ollamaDir to session PATH" } } } function Invoke-Download { param( [string]$Url, [string]$OutFile ) Write-Status " Downloading: $Url" try { $request = [System.Net.HttpWebRequest]::Create($Url) $request.AllowAutoRedirect = $true $response = $request.GetResponse() $totalBytes = $response.ContentLength $stream = $response.GetResponseStream() $fileStream = [System.IO.FileStream]::new($OutFile, [System.IO.FileMode]::Create) $buffer = [byte[]]::new(65536) $totalRead = 0 $lastUpdate = [DateTime]::MinValue $barWidth = 40 try { while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) { $fileStream.Write($buffer, 0, $read) $totalRead += $read $now = [DateTime]::UtcNow if (($now - $lastUpdate).TotalMilliseconds -ge 250) { if ($totalBytes -gt 0) { $pct = [math]::Min(100.0, ($totalRead / $totalBytes) * 100) $filled = [math]::Floor($barWidth * $pct / 100) $empty = $barWidth - $filled $bar = ('#' * $filled) + (' ' * $empty) $pctFmt = $pct.ToString("0.0") Write-Host -NoNewline "`r$bar ${pctFmt}%" } else { $sizeMB = [math]::Round($totalRead / 1MB, 1) Write-Host -NoNewline "`r${sizeMB} MB downloaded..." } $lastUpdate = $now } } # Final progress update if ($totalBytes -gt 0) { $bar = '#' * $barWidth Write-Host "`r$bar 100.0%" } else { $sizeMB = [math]::Round($totalRead / 1MB, 1) Write-Host "`r${sizeMB} MB downloaded. " } } finally { $fileStream.Close() $stream.Close() $response.Close() } } catch { if ($_.Exception -is [System.Net.WebException]) { $webEx = [System.Net.WebException]$_.Exception if ($webEx.Response -and ([System.Net.HttpWebResponse]$webEx.Response).StatusCode -eq [System.Net.HttpStatusCode]::NotFound) { throw "Download failed: not found at $Url" } } if ($_.Exception.InnerException -is [System.Net.WebException]) { $webEx = [System.Net.WebException]$_.Exception.InnerException if ($webEx.Response -and ([System.Net.HttpWebResponse]$webEx.Response).StatusCode -eq [System.Net.HttpStatusCode]::NotFound) { throw "Download failed: not found at $Url" } } throw "Download failed for ${Url}: $($_.Exception.Message)" } } # -------------------------------------------------------------------------- # Uninstall # -------------------------------------------------------------------------- function Invoke-Uninstall { Write-Step "Uninstalling Ollama" $regKey = Find-InnoSetupInstall if (-not $regKey) { Write-Host ">>> Ollama is not installed." return } $uninstallString = (Get-ItemProperty -Path $regKey).UninstallString if (-not $uninstallString) { Write-Warning "No uninstall string found in registry" return } # Strip quotes if present $uninstallExe = $uninstallString -replace '"', '' Write-Status " Uninstaller: $uninstallExe" if (-not (Test-Path $uninstallExe)) { Write-Warning "Uninstaller not found at: $uninstallExe" return } Write-Host ">>> Launching uninstaller..." # Run with GUI so user can choose whether to keep models Start-Process -FilePath $uninstallExe -Wait # Verify removal if (Find-InnoSetupInstall) { Write-Warning "Uninstall may not have completed" } else { Write-Host ">>> Ollama has been uninstalled." } } # -------------------------------------------------------------------------- # Install # -------------------------------------------------------------------------- function Invoke-Install { # Determine installer URL if ($Version) { $installerUrl = "$DownloadBaseURL/OllamaSetup.exe?version=$Version" } else { $installerUrl = "$DownloadBaseURL/OllamaSetup.exe" } # Download installer Write-Step "Downloading Ollama" if (-not $DebugInstall) { Write-Host ">>> Downloading Ollama for Windows..." } $tempInstaller = Join-Path $env:TEMP "OllamaSetup.exe" Invoke-Download -Url $installerUrl -OutFile $tempInstaller # Verify signature Write-Step "Verifying signature" if (-not (Test-Signature -FilePath $tempInstaller)) { Remove-Item $tempInstaller -Force -ErrorAction SilentlyContinue throw "Installer signature verification failed" } # Build installer arguments $installerArgs = "/VERYSILENT /NORESTART /SUPPRESSMSGBOXES" if ($InstallDir) { $installerArgs += " /DIR=`"$InstallDir`"" } Write-Status " Installer args: $installerArgs" # Run installer Write-Step "Installing Ollama" if (-not $DebugInstall) { Write-Host ">>> Installing Ollama..." } # Create upgrade marker so the app starts hidden # The app checks for this file on startup and removes it after $markerDir = Join-Path $env:LOCALAPPDATA "Ollama" $markerFile = Join-Path $markerDir "upgraded" if (-not (Test-Path $markerDir)) { New-Item -ItemType Directory -Path $markerDir -Force | Out-Null } New-Item -ItemType File -Path $markerFile -Force | Out-Null Write-Status " Created upgrade marker: $markerFile" # Start installer and wait for just the installer process (not children) # Using -Wait would wait for Ollama to exit too, which we don't want $proc = Start-Process -FilePath $tempInstaller ` -ArgumentList $installerArgs ` -PassThru $proc.WaitForExit() if ($proc.ExitCode -ne 0) { Remove-Item $tempInstaller -Force -ErrorAction SilentlyContinue throw "Installation failed with exit code $($proc.ExitCode)" } # Cleanup Remove-Item $tempInstaller -Force -ErrorAction SilentlyContinue # Update PATH in current session so 'ollama' works immediately Write-Step "Updating session PATH" Update-SessionPath Write-Host ">>> Install complete. Run 'ollama' from the command line." } # -------------------------------------------------------------------------- # Main # -------------------------------------------------------------------------- if ($Uninstall) { Invoke-Uninstall } else { Invoke-Install }