PowerShell – Script to install configure and uninstall FortiClient VPN

Below is a single, self‑contained PowerShell 5.1 script that:
- Installs FortiClient VPN (MSI or extracted MSI from the online EXE) silently
- Creates an SSL‑VPN profile with a gateway and port (writes the FortiClient.conf profile)
- Verifies installation and logs actions
- Uninstalls FortiClient VPN cleanly by locating the MSI product and running msiexec uninstall
Use it as an admin on the target machine. The script is idempotent and includes logging. I also include short usage examples after the script.
Notes
- Fortinet’s VPN‑only installer can be run silently; the MSI can be used with msiexec flags for unattended install.
- FortiClient VPN profiles are commonly deployed by writing the FortiClient.conf under
C:\ProgramData\Fortinet\FortiClient\vpn\. The script writes that file.
<# .SYNOPSIS Install, configure (gateway + port), verify, and uninstall FortiClient VPN (PowerShell 5.1). .DESCRIPTION Single turnkey script. Set parameters below and run elevated. - Installs from MSI or extracts MSI from EXE if needed. - Writes an SSL-VPN profile to FortiClient.conf. - Can uninstall by product GUID lookup. .NOTES Tested for PowerShell 5.1. Run as Administrator. #> #region Parameters - edit these before running # Path to installer. Can be an MSI or the FortiClientVPN online EXE (script will try to extract MSI). $InstallerPath = "C:\Temp\FortiClientVPN.exe" # VPN profile settings $ProfileName = "MyVPN" $GatewayHost = "vpn.example.com" $GatewayPort = 443 # Action: "install" or "uninstall" $Action = "install" # Logging $LogFolder = "C:\Temp\FortiClientDeploy" $VerbosePreference = "Continue" #endregion # Ensure log folder if (!(Test-Path $LogFolder)) { New-Item -Path $LogFolder -ItemType Directory -Force | Out-Null } $LogFile = Join-Path $LogFolder "FortiClientDeploy-$(Get-Date -Format yyyyMMdd-HHmmss).log" function Log { param([string]$Message) $ts = (Get-Date).ToString("s") "$ts`t$Message" | Tee-Object -FilePath $LogFile -Append } function Extract-MsiFromExe { param([string]$ExePath, [string]$OutFolder) Log "Attempting to extract MSI from EXE: $ExePath" if (!(Test-Path $ExePath)) { throw "Installer not found: $ExePath" } if (!(Test-Path $OutFolder)) { New-Item -Path $OutFolder -ItemType Directory -Force | Out-Null } # Many FortiClient online installers extract to %LocalAppData%\Temp or create a cache folder. # We'll run the EXE with /quiet to force extraction then search temp for an MSI. $proc = Start-Process -FilePath $ExePath -ArgumentList "/quiet" -PassThru -Wait -WindowStyle Hidden Start-Sleep -Seconds 3 # Search common extraction locations for FortiClient*.msi $candidates = @() $candidates += Get-ChildItem -Path "$env:LOCALAPPDATA\Temp" -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "FortiClient.*\.msi$" } | Select-Object -First 5 $candidates += Get-ChildItem -Path "C:\ProgramData\Applications\Cache" -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "FortiClient.*\.msi$" } | Select-Object -First 5 if ($candidates.Count -eq 0) { Log "No MSI found after extraction attempt." return $null } $msi = $candidates[0].FullName $dest = Join-Path $OutFolder (Split-Path $msi -Leaf) Copy-Item -Path $msi -Destination $dest -Force Log "Extracted MSI to $dest" return $dest } function Install-FortiClientMsi { param([string]$MsiPath) Log "Installing MSI: $MsiPath" if (!(Test-Path $MsiPath)) { throw "MSI not found: $MsiPath" } # Install VPN-only if MSI supports ADDLOCAL=VPN; otherwise do a quiet install. $args = "/i `"$MsiPath`" ADDLOCAL=VPN REBOOT=ReallySuppress /qn /L*v `"$LogFolder\FortiClient-install.log`"" Log "Running: msiexec.exe $args" $p = Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -PassThru if ($p.ExitCode -eq 0) { Log "Install completed successfully." } else { Log "Install returned exit code $($p.ExitCode). Check installer log." throw "Installation failed with exit code $($p.ExitCode)" } } function Write-VpnProfile { param([string]$Name, [string]$Host, [int]$Port) $configPath = "C:\ProgramData\Fortinet\FortiClient\vpn\FortiClient.conf" $folder = Split-Path $configPath if (!(Test-Path $folder)) { New-Item -Path $folder -ItemType Directory -Force | Out-Null } # Build server string with host:port $server = if ($Port -and $Port -ne 0) { "$Host:$Port" } else { $Host } $xml = @" <forticlient_configuration> <vpn> <sslvpn> <connections> <connection> <name>$Name</name> <server>$server</server> <description>Deployed by script</description> <prompt_certificate>0</prompt_certificate> </connection> </connections> </sslvpn> </vpn> </forticlient_configuration> "@ $xml | Out-File -FilePath $configPath -Encoding utf8 -Force Log "Wrote VPN profile to $configPath (name=$Name server=$server)" } function Get-FortiClientProduct { Log "Querying installed FortiClient product via WMI" $apps = Get-WmiObject -Class Win32_Product -Filter "Name LIKE 'FortiClient%'" -ErrorAction SilentlyContinue return $apps } function Uninstall-FortiClient { Log "Attempting uninstall" $apps = Get-FortiClientProduct if (!$apps) { Log "No FortiClient product found." return } foreach ($app in $apps) { $guid = $app.IdentifyingNumber Log "Uninstalling $($app.Name) GUID $guid" $args = "/x $guid /qn /L*v `"$LogFolder\FortiClient-uninstall.log`" REBOOT=ReallySuppress" $p = Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -PassThru Log "Uninstall exit code $($p.ExitCode)" } } # Main flow try { Log "Script started. Action = $Action" if ($Action -eq "install") { # Determine MSI path if ($InstallerPath -match "\.msi$") { $msi = $InstallerPath } else { # Try to extract MSI from EXE $extractFolder = Join-Path $LogFolder "extracted" $msi = Extract-MsiFromExe -ExePath $InstallerPath -OutFolder $extractFolder if (-not $msi) { # If extraction failed, try running EXE directly with quiet flags (some EXEs support /quiet) Log "Falling back to running EXE directly with /quiet" $args = "/quiet /norestart" $p = Start-Process -FilePath $InstallerPath -ArgumentList $args -Wait -PassThru if ($p.ExitCode -ne 0) { throw "EXE installer failed with exit code $($p.ExitCode)" } Log "EXE installer completed; attempting to detect installed product." } } if ($msi) { Install-FortiClientMsi -MsiPath $msi } # Write VPN profile Write-VpnProfile -Name $ProfileName -Host $GatewayHost -Port $GatewayPort # Verify install $installed = Get-FortiClientProduct if ($installed) { Log "FortiClient installed: $($installed.Name) version $($installed.Version)" } else { Log "FortiClient not detected after install. Check logs." } Log "Install action complete." } elseif ($Action -eq "uninstall") { Uninstall-FortiClient Log "Uninstall action complete." } else { throw "Unknown action: $Action. Use 'install' or 'uninstall'." } } catch { Log "ERROR: $($_.Exception.Message)" throw } finally { Log "Script finished." }Quick usage examples
- Install using an extracted MSI already on disk:
- Edit
$InstallerPath = "C:\Temp\FortiClientVPN.msi"and$Action = "install", then run elevated.- Install using the online EXE:
- Set
$InstallerPath = "C:\Temp\FortiClientVPN.exe"and$Action = "install". The script will attempt to extract the MSI and fall back to running the EXE quietly if extraction fails.- Uninstall:
- Set
$Action = "uninstall"and run elevated. The script finds FortiClient products via WMI and callsmsiexec /x {GUID}silently.Implementation details and caveats
- Silent install flags
- MSI:
msiexec /i "FortiClient.msi" ADDLOCAL=VPN REBOOT=ReallySuppress /qnis commonly used for VPN‑only installs; logs can be captured with/L*v.- EXE extraction
- Fortinet’s online EXE often extracts an MSI into a temp/cache folder; the script searches common locations and copies the MSI for a controlled install. If extraction fails, the script attempts a quiet EXE run.
- Profile deployment
- Writing
FortiClient.confunderC:\ProgramData\Fortinet\FortiClient\vpn\is a practical way to preconfigure SSL‑VPN connections when EMS is not used. Adjust XML fields to match your environment.- Uninstall detection
- The script uses WMI
Win32_Productto find installed FortiClient entries and uninstalls by GUID. In large environments you may prefer to query the registry uninstall keys for performance.Uninstall FortiClient VPN
FortiClient registers an uninstall GUID. You can query it and remove it silently.
Uninstall‑FortiClientVPN.ps1
$App = Get-WmiObject Win32_Product | Where-Object { $_.Name -like "FortiClient*" } if ($App) { Start-Process msiexec.exe -ArgumentList @( "/x",$App.IdentifyingNumber, "/qn", "/L*v C:\Temp\FortiClientVPN-uninstall.log" ) -Wait } else { Write-Host "FortiClient VPN not found." }