Deploy Always-On OpenVPN via Group Policy

· 991 words · 5 minute read

Unlike those enterpris-y VPN solutions that offer very easy ways to install them on all laptops and phones, OpenVPN (at least the community edition) provides a MSI, a service, and nothing else. This blog shares my experience in deploying always-on OpenVPN over group policy with zero user interaction.

The deployment consists of three parts:

  1. Deploy OpenVPN profiles and certificates
  2. Set OpenVPN service registry (strictly speaking: optional)
  3. Deploy unattended MSI install

This assumes you have a working OpenVPN setup and PKI.

Before replicating the settings over your machines, install OpenVPN (full installation is OK) on a test machine and run C:\Program Files\OpenVPN\bin\openvpn.exe \path\to\config.ovpn to test it.

Note that I am using cryptoapicert "SUBJ:YUUTA.MOE" for the client to obtain the machine certificated issued by ADCS. You may do something else (like hard coding the private key file), but it may not scale very well. This option is actually not very easy to use because it does not support searching certs by ADCS certificate template (see Patch ). It does supports filtering with ISSUER:, but it turns out that it is always selecting some mysterious machine certificates that are located on a smart card (and thus prompting me to insert the smart card, which I don’t have). There is also no debug logs to show which certificate it is trying to get. Anyways, the code is at cryptoapi.c:203 . I am just using subjet match, which is not very well, but it works in my simple environment.

Directory structures used in this deployment:

  • C:\Program Files\OpenVPN\: binaries.
  • C:\VPN\vpn.ovpn: Profile.
  • C:\VPN\ca.crt: PEM-encoded root CA.
  • C:\VPN\ca.crt: PEM-encoded root CA.

Deploy profiles #

This step is basically group policy file deployment. Just use the Files policy in Computer Configuration > Preferences > Windows Settings. The good-old Windows XP GUI made my great-great-great-grandmother happy. lol.

Nothing to point out here. Make sure you use UNC path. The directory will be automatically created it not existing, so no need to do that.

Set registry #

OpenVPN ships with a very nice OpenVPNService which automatically reads some system profiles from a designated location and starts them. This requires no user interaction (and the OpenVPN GUI component is completely unused as well).

This service best fits in our deployment senario, and it is also automatically started once installed, so no need to enable that service manually over GPO.

By default, it reads configuration files in C:\Program Files\OpenVPN\config-auto\*.ovpn and writes logs to C:\Program Files\OpenVPN\log\*.log. This works, but in my senario I want to tweak that a little bit, so it uses the C:\VPN path.

This is supported on their Docs . The only thing needed is the keys in HKEY_LOCAL_MACHINE\SOFTWARE\OpenVPN. Read that docs yourselves and change the location, or you can keep the default.

The good-old-XP-style GPO registry policy is your friend. No worries if installing the MSI (which happens at boot, after the registry policy is applied) will reset the values - it won’t, but uninstalling it will delete the whole OpenVPN key. Keep that in mind.

Install MSI #

Group policy MSI deployment sucks, but I don’t have SCCM, so that is the only choice. The routine MSI deployment stuff went pretty normal, until I need to specify the components to install.

Yes, the MSI by default installs lots of documentation and also the naughting GUI with it. Whenever a user logs on, the OpenVPN gui will prompt a dialog telling that there is no per-user configuration set. I definitely don’t want anything user-interactive, so I have to remove that component. This is done by the ADDLOCAL variable when installing the MSI.

It turns out that you can use the MSI logging function to see the actual value of that variable. Just do msiexec /i OpenVPN.msi /l*v log.txt and choose your components then install, and you will see the selection in the log


Lastly you have to tell GPO to use that variable. This is actually pretty complicated (probably how all MSFT products are designed). You need to use a tool called Orca from Windows SDK, load the MSI, create a transformation, set the ADDLOCAL property, generate a binary MST file, and finally load that file into GPO.

So basically you install Windows SDK with the MSI component installed. This does not install Orca directly, but instead it extracts an Orca MSI into C:\Program Files\Windows Kits\10\. Install that, and you will find Orca in program files x86 Source.

Open that, and load the MSI. Then click on menu Transform -> New Transform.

Then locate to the Property table and right-click the list in the right pane, and click on Add Row.

Type ADDLOCAL and your value in it.

Finally click on Transform menu again, Generate Transform, save that MST to your sysvol.

Lastly, go to the GPO software installation item property, add the MST.

See for some screenshots.

That’s it, hope everything works.

Troubleshooting: DC sync #

You can’t wait for DC sync. For LDAP sync, use repadmin /syncall /AdeP. Remove P if you are running on a RODC. This does not seem to work all the time, so you can click on the sync menus in AD Sites and Services MMC to achieve eventual consistency.

For sysvol:

Sync-DfsReplicationGroup -GroupName "Domain System Volume" -SourceComputerName From -DestinationComputerName To -DurationInMinutes 5 -Verbose

Troubleshooting: Wait for network #

You definitely saw something like “the group policy client side extension software installation was unable to apply” before.

Enable Computer Preferences > Templates > System > Logon > Always wait for the network at computer startup and logon.

And reboot multiple times? (I’m on WiFi)

Troubleshooting: msiexec problems #

“This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows intaller package.” when executing msiexec /i.

Do not use msiexec /i .\OpenVPN.msi. Remove the .\ part does the trick.

Or have a look at msiexec policies? I used to have problems with them.