Registry keys are not created via Powershell

Hi together!

We have to create a VPN tunnel in a special way.
Now we have put together a script that actually serves the purpose, but part of it doesn’t work. The beginning works - VPN connection is created including route. This is created with Add-VpnConnection and Route.

But now we still need AlwaysOn. We would like to create this with the script attached below.
If you run the sections individually, it works and the registry keys are created.
If you run the script as a whole - not a single registry entry is created and there is no error output.
I’ve already tried it with and without a transaction…

What the hell is wrong here?

$ProfileName = "VPNNAME"

# Validate VPN profile
Write-Verbose "Searching VPN profiles for `"$ProfileName`"."

If ($AllUserConnection) {

# Get VPN profile running in the user's context
    $Vpn = Get-VpnConnection -Name $ProfileName -AllUserConnection -ErrorAction SilentlyContinue

}

Else {

# Get VPN profile running in the 'all users' context
    $Vpn = Get-VpnConnection -Name $ProfileName -ErrorAction SilentlyContinue

}

If ($Null -eq $Vpn) {

# Exit if VPN profile does not exist
    Write-Warning "VPN connection `"$ProfileName`" not found."
    Return

}

Else {

Write-Verbose "VPN connection `"$ProfileName`" found."

}

# Use transaction for registry updates
Start-Transaction

#Create registry
REG add "HKLM\System\CurrentControlSet\Services\RasMan\Config\"

# Search AutoTriggerDisabledProfilesList for VPN profile
$Path = 'HKLM:\System\CurrentControlSet\Services\RasMan\Config\'
$Name = 'AutoTriggerDisabledProfilesList'

Write-Verbose "Searching $Name in $Path for VPN profile `"$ProfileName`"..."

Try {

# Get the current registry values as an array of strings
    [string[]]$DisabledProfiles = Get-ItemPropertyValue -Path $Path -Name $Name -ErrorAction Stop

}

Catch {

Write-Verbose "$Name does not exist in $Path. No action required."
    Return

}

If ($DisabledProfiles) {

# Create ordered hashtable
    $List = [Ordered]@{}
    $DisabledProfiles | ForEach-Object { $List.Add("$($_.ToLower())", $_) }

# Search hashtable for matching VPN profile and remove if present
    If ($List.Contains($ProfileName)) {

Write-Verbose 'Profile found. Removing entry...'
        $List.Remove($ProfileName)
        Write-Verbose 'Updating the registry...'
        Set-ItemProperty -Path $Path -Name $Name -Value $List.Values -UseTransaction

}

}

Else {

Write-Verbose "No profiles found matching `"$ProfileName`"."
    Return

}

# Add user SID to registry
If ($AllUserConnection) {

$SID = 'S-1-1-0'
    Write-Verbose "Adding SYSTEM SID $SID to registry..."

}

Else {

Try {

$SID = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
        Write-Verbose "Adding user SID $SID to registry..."

}

Catch {

Write-Warning $_.Exception.Message
        Return

}

}

$Parameters = @{

Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'UserSID'
    PropertyType   = 'String'
    Value          = $SID
    UseTransaction = $True

}

New-ItemProperty @Parameters -Force | Out-Null

# Add VPN profile name to registry
$Parameters = @{

Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'AutoTriggerProfileEntryName'
    PropertyType   = 'String'
    Value          = $ProfileName
    UseTransaction = $True

}

New-ItemProperty @Parameters | Out-Null

# Add VPN profile GUID to registry
Write-Verbose "Adding VPN GUID $GUID to registry..."
[guid]$GUID = $Vpn | Select-Object -ExpandProperty Guid
$Binary = $Guid.ToByteArray()

$Parameters = @{

Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'AutoTriggerProfileGUID'
    PropertyType   = 'Binary'
    Value          = $Binary
    UseTransaction = $True

}

New-ItemProperty @Parameters | Out-Null

# Add phonebook path to registry
If ($AllUserConnection) {

$Path = Join-Path -Path $env:programdata -ChildPath Microsoft\Network\Connections\Pbk\rasphone.pbk
    Write-Verbose "RAS phonebook path is $Path."

}

Else {

$Path = Join-Path -Path $env:userprofile -ChildPath AppData\Roaming\Microsoft\Network\Connections\Pbk\rasphone.pbk
    Write-Verbose "RAS phonebook path is $Path."

}

$Parameters = @{

Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'AutoTriggerProfilePhonebookPath'
    PropertyType   = 'String'
    Value          = $Path
    UseTransaction = $True

}

New-ItemProperty @Parameters | Out-Null

# Commit registry changes
Complete-Transaction

# Disable the "Disconnect" button in VAN UI/Settings > ensuring that "Connect Automatically" cannot be unchecked
(get-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk") |ForEach-Object {$_ -Replace "Options=0", "Options=18"} | set-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk"

Welcome.

For those who don’t know I think this post is referring to Microsoft’s ‘Always On VPN’ solution: About Always On VPN for Windows Server Remote Access | Microsoft Learn

When you say you split it up and it works, how did you ‘split it up’? If you step through your code one line at a time I presume it works? Could you share the entire code? I think some of it is missing based on $AllUserConnection not being defined anywhere (guessing this is maybe a switch or some other logic handled in another part of the code that isn’t shared).

On the systems you are running it on, is it possible your return statements are triggering, thus not continuing the rest of the script? Based on a quick glance, it looks like that it would exit the script entirely potentially (which may be desired, based on what I’m seeing). We’d probably need the rest of the code to be sure though. There seem to be several points in the script where this may happen. As you may know, when you use the keyword return it will exit the current scope: about Return - PowerShell | Microsoft Learn.

I’d probably suggest splitting this up into at least two parts/functions. I’m a big fan of creating a function that does one thing, and one thing well.

Part 1 would be the VPN part. Make that separate and wrap it into its own function, that perhaps does an output if it’s able to find it with whatever details you need.
Part 2 would be the registry part, and if desired, you only call that part when the first function returns some sort of output.

This will help you organize your code in a way that makes it easier to follow. You then can more easily dissect what’s happening I think when you call it as a whole.

Thanks for sharing about the transactions feature in PS. I actually did not know about that. I see you mentioned you tried with and without it… is it desired to use that feature or not use it? I don’t know what troubles that may introduce, but it seems like it could be useful if you wanted a set of ‘actions/changes’ to all be completed or if something happened, make sure that nothing changes.

1 Like

Hello dotnVo,

Thank you very much for your feedback.
Unfortunately, AlwayOn doesn’t work for us - long story short, Always On via intune is only supported with IKEv2, unfortunately only L2TP is currently possible for us - I’ve been with Microsoft Support for nine weeks - which, among other things, also has the corresponding script has made.

Unfortunately, we cannot find out the corresponding error.
I would like to attach the entire code straight away.

Effectively, if I execute command by command block by block, it works - but not all in one go.

We have already gone through everything with the debugger and also with Return - without success.

Unfortunately, dismantling the code - i.e. running the VPN part separately - didn’t produce the desired result.

Today I received the following message from the MS engineer:
“TLDR Update: The PowerShell script works - as soon as you manually click on the “Connect Automatically” option in the VAN/Settings UI. You can then deactivate it again (remove the tick), then the PowerShell script also works. This is because the Clicking on “Connect Automatically” does a lot more than create registry entries (various WMI changes, syscalls, etc.”

So it seems almost hopeless to do this without hands-on from the employees.

#Create VPN Tunnel and configure Routes
Add-VpnConnection -Name Schulen_VPN -ServerAddress SERVERADDRESS.dyndns -TunnelType L2tp -AuthenticationMethod Pap -EncryptionLevel Optional -L2tpPsk PASSWORD -RememberCredential -SplitTunneling -Force
Add-VpnConnectionRoute -Name Schulen_VPN -DestinationPrefix 1.1.1.1/32 -RouteMetric 1 -PassThru
Add-VpnConnectionRoute -Name Schulen_VPN -DestinationPrefix 1.1.1.1/32 -RouteMetric 1 -PassThru
Add-VpnConnectionRoute -Name Schulen_VPN -DestinationPrefix 1.1.1.1/32 -RouteMetric 1 -PassThru
(get-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk") | ForEach-Object {$_ -Replace "AutoTiggerCapable=0", "AutoTiggerCapable=1"} | set-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk"
(get-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk") | ForEach-Object {$_ -Replace "AlwaysOnCapable=0", "AlwaysOnCapable=1"} | set-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk"


$ProfileName = "Schulen_VPN"

# Validate VPN profile
Write-Verbose "Searching VPN profiles for `"$ProfileName`"."

If ($AllUserConnection) {

    # Get VPN profile running in the user's context
    $Vpn = Get-VpnConnection -Name $ProfileName -AllUserConnection -ErrorAction SilentlyContinue

}

Else {

    # Get VPN profile running in the 'all users' context
    $Vpn = Get-VpnConnection -Name $ProfileName -ErrorAction SilentlyContinue

}

If ($Null -eq $Vpn) {

    # Exit if VPN profile does not exist
    Write-Warning "VPN connection `"$ProfileName`" not found."
    Return

}

Else {

    Write-Verbose "VPN connection `"$ProfileName`" found."

}

# Use transaction for registry updates
Start-Transaction

#Create registry
REG add "HKLM\System\CurrentControlSet\Services\RasMan\Config\"

# Search AutoTriggerDisabledProfilesList for VPN profile
$Path = 'HKLM:\System\CurrentControlSet\Services\RasMan\Config\'
$Name = 'AutoTriggerDisabledProfilesList'

Write-Verbose "Searching $Name in $Path for VPN profile `"$ProfileName`"..."

Try {

    # Get the current registry values as an array of strings
    [string[]]$DisabledProfiles = Get-ItemPropertyValue -Path $Path -Name $Name -ErrorAction Stop

}

Catch {

    Write-Verbose "$Name does not exist in $Path. No action required."
    Return

}

If ($DisabledProfiles) {

    # Create ordered hashtable
    $List = [Ordered]@{}
    $DisabledProfiles | ForEach-Object { $List.Add("$($_.ToLower())", $_) }

    # Search hashtable for matching VPN profile and remove if present
    If ($List.Contains($ProfileName)) {

        Write-Verbose 'Profile found. Removing entry...'
        $List.Remove($ProfileName)
        Write-Verbose 'Updating the registry...'
        Set-ItemProperty -Path $Path -Name $Name -Value $List.Values -UseTransaction

    }

}

Else {

    Write-Verbose "No profiles found matching `"$ProfileName`"."
    Return

}

# Add user SID to registry
If ($AllUserConnection) {

    $SID = 'S-1-1-0'
    Write-Verbose "Adding SYSTEM SID $SID to registry..."

}

Else {

    Try {

        $SID = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
        Write-Verbose "Adding user SID $SID to registry..."

    }

    Catch {

        Write-Warning $_.Exception.Message
        Return

    }

}

$Parameters = @{

    Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'UserSID'
    PropertyType   = 'String'
    Value          = $SID
    UseTransaction = $True

}

New-ItemProperty @Parameters -Force | Out-Null

# Add VPN profile name to registry
$Parameters = @{

    Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'AutoTriggerProfileEntryName'
    PropertyType   = 'String'
    Value          = $ProfileName
    UseTransaction = $True

}

New-ItemProperty @Parameters | Out-Null

# Add VPN profile GUID to registry
Write-Verbose "Adding VPN GUID $GUID to registry..."
[guid]$GUID = $Vpn | Select-Object -ExpandProperty Guid
$Binary = $Guid.ToByteArray()

$Parameters = @{

    Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'AutoTriggerProfileGUID'
    PropertyType   = 'Binary'
    Value          = $Binary
    UseTransaction = $True

}

New-ItemProperty @Parameters | Out-Null

# Add phonebook path to registry
If ($AllUserConnection) {

    $Path = Join-Path -Path $env:programdata -ChildPath Microsoft\Network\Connections\Pbk\rasphone.pbk
    Write-Verbose "RAS phonebook path is $Path."

}

Else {

    $Path = Join-Path -Path $env:userprofile -ChildPath AppData\Roaming\Microsoft\Network\Connections\Pbk\rasphone.pbk
    Write-Verbose "RAS phonebook path is $Path."

}

$Parameters = @{

    Path           = 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\Config\'
    Name           = 'AutoTriggerProfilePhonebookPath'
    PropertyType   = 'String'
    Value          = $Path
    UseTransaction = $True

}

New-ItemProperty @Parameters | Out-Null

# Commit registry changes
Complete-Transaction

# Disable the "Disconnect" button in VAN UI/Settings > ensuring that "Connect Automatically" cannot be unchecked
(get-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk") |ForEach-Object {$_ -Replace "Options=0", "Options=18"} | set-content -Path "$env:appdata\Microsoft\Network\Connections\Pbk\rasphone.pbk"

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.