#
.SYNOPSIS
Patches all Keyence VR-series .exe.config files to opt the app out of .NET
FIPS policy enforcement so the EEPROM MD5 calibration check does not throw
'This implementation is not part of the Windows Platform FIPS validated
cryptographic algorithms.'
Currently approved only for VR-3000 G2. Before running on a production
bay, report the PC hostname to GE InfoSec and confirm the bay is on
the documented FIPS-exception list. Imaging-time auto-apply (in
09-Setup-Keyence.ps1) gates this script behind model=vr3000.
.DESCRIPTION
Scans C:\Program Files\KEYENCE and C:\Program Files (x86)\KEYENCE for any
.exe.config under VR-3000 G2, VR-5000, VR-6000 (and any other Keyence model
that ships in the same root). Injects:
as a sibling under . Preserves any existing
children (generatePublisherEvidence, useLegacyJit, etc).
Idempotent. Re-running is a no-op for already-patched configs.
This patch is SCOPED to the Keyence app's CLR. It does NOT disable the
OS-wide LSA FIPS policy. Intune / Defender baseline policies that enforce
the LSA key remain in effect.
.PARAMETER InstallRoots
Override the default search roots. By default both C:\Program Files\KEYENCE
and C:\Program Files (x86)\KEYENCE are scanned.
.PARAMETER WhatIf
Show what would be patched without writing any files.
.EXAMPLE
.\Patch-KeyenceFipsConfigs.ps1
.\Patch-KeyenceFipsConfigs.ps1 -WhatIf
.\Patch-KeyenceFipsConfigs.ps1 -InstallRoots 'D:\KEYENCE'
.NOTES
Affected EXEs (Profilometer3.exe, VRAnalyzer*.exe, VRInspection_*.exe, etc.)
must be restarted after the patch for the new config to take effect.
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[string[]]$InstallRoots = @(
'C:\Program Files\KEYENCE',
'C:\Program Files (x86)\KEYENCE'
)
)
$ErrorActionPreference = 'Continue'
$patched = 0
$skipped = 0
$errors = 0
foreach ($root in $InstallRoots) {
if (-not (Test-Path -LiteralPath $root)) {
Write-Host "Root not present, skipping: $root"
continue
}
Write-Host "Scanning: $root"
$configs = Get-ChildItem -LiteralPath $root -Filter '*.exe.config' -Recurse -ErrorAction SilentlyContinue
foreach ($cfg in $configs) {
try {
[xml]$xml = Get-Content -LiteralPath $cfg.FullName
$cfgRoot = $xml.configuration
if (-not $cfgRoot) {
Write-Warning " [SKIP] No root: $($cfg.FullName)"
$errors++
continue
}
$rt = $cfgRoot.SelectSingleNode('runtime')
if (-not $rt) {
$rt = $xml.CreateElement('runtime')
$cfgRoot.AppendChild($rt) | Out-Null
}
$existing = $rt.SelectSingleNode("enforceFIPSPolicy[@enabled='false']")
if ($existing) {
Write-Host " [SKIP] already patched: $($cfg.FullName)"
$skipped++
continue
}
# Remove any other enforceFIPSPolicy variants (enabled=true or no attr).
$rt.SelectNodes('enforceFIPSPolicy') | ForEach-Object { $rt.RemoveChild($_) | Out-Null }
$fips = $xml.CreateElement('enforceFIPSPolicy')
$fips.SetAttribute('enabled', 'false')
$rt.AppendChild($fips) | Out-Null
if ($PSCmdlet.ShouldProcess($cfg.FullName, 'Inject enforceFIPSPolicy')) {
$xml.Save($cfg.FullName)
Write-Host " [OK] patched: $($cfg.FullName)"
$patched++
} else {
Write-Host " [WHATIF] would patch: $($cfg.FullName)"
}
} catch {
Write-Warning " [ERR] $($cfg.FullName): $_"
$errors++
}
}
}
Write-Host ""
Write-Host "Summary: patched=$patched skipped=$skipped errors=$errors"
Write-Host ""
Write-Host "Restart Keyence apps (Profilometer3.exe, VRAnalyzer*.exe, VRLauncher.exe,"
Write-Host "VRInspection_*.exe, etc.) for the new config to take effect. No reboot needed."