Skip to content

Author: Alexander Rismondo

Auto Update Minimum OS Version in Compliance Policy

So, one of my customers requested that the compliance policy must be always up to date.
The minimum accepted OS version shouldn’t be older than the last ~4 patches, as compliance will be used for various Conditional Access policies.

Sounds simple enough? Well, not really as Intune doesn’t offer this option out of the box.
All you get is the possibility to define a static minimum version.

Azure Automation and Graph PowerShell to the rescue!

How will this work? Well, Azure Automation has the possibility to use a system assigned identity, this identity can be granted

So first things first, we deploy an Azure Automation Account in our subscription:

Automation Account in Marketplace

After filling in the basics, we can swap to the advanced tab, to enable the System assigned identity:

Create Automation Account
Click System assigned Identity

After we hit create, our Automation Account is deployed and we can open it, personally I prefer the new “Runtime Environment Experience”, therefore I will switch to it.

Try Runtime Environment Experience

Next step is to create our empty Workbook:

During the creation of the Runbook we will have, if you did option to the new experience, the option the create a Runtime Enviroment.

After giving the Runtime Enviroment a fitting name, we can now add PowerShell Modules to it.

I’ve added 3 Modules that we will need for our Compliance Policy Update script:
Microsoft.Graph.Authentication, Microsoft.Graph.DeviceManagement and PSParseHTML.

Now it’s time to create the Runbook and edit it.

The script will download the latest Version Information from the Microsoft Learn site and update the compliance policy with the specified version.

You’ll need the id of your compliance policy and you need to specify how many versions back will be compliant.

$compliancePolicyId = "60bf37e1-fcc4-418a-b8d2-afee15592426"
$oldVersions = 4

Function Get-LatestWinBuild {
    param (
        [string]$latestUri = "https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information",
        [string]$tableId = "historyTable_0",
        [int]$buildIndex,
        [int]$winVersion
    )
    if ($winVersion -eq 10) {
        $latestUri = "https://learn.microsoft.com/en-us/windows/release-health/release-information"
    }
    $output = ConvertFrom-HTMLTag -Id $tableId -Url $latestUri -ReturnObject
    $output = ConvertFrom-HtmlTable -Content $output.OuterHtml

    return [string] "10.0.$($output.Build[$buildIndex])"

}

$winBuild = Get-LatestWinBuild -buildIndex $oldVersions

if ($winBuild){
    Write-Output "Latest found build: $winBuild"
} else {
    Write-Output "Error fetching build number."
}

Connect-MgGraph -Identity -NoWelcome

if ($winBuild -match "\d\d.\d.\d\d\d\d\d.\d\d\d\d") {
    $params = @{
        "@odata.type"    = "#microsoft.graph.windows10CompliancePolicy"
        osMinimumVersion = $winBuild
    }
    Write-Output "Updating compliance policy: $compliancePolicyId setting osMinimumVersion to $winBuild"
    Update-MgDeviceManagementDeviceCompliancePolicy -DeviceCompliancePolicyId $compliancePolicyId -BodyParameter $params
}
else {
    #trigger some alert, because version number seems invalid.
    Write-Output "Error."
}
PowerShell

After saving the Runbook, we can specify a Job trigger.

I opted for a Weekly update.

We’re not done yet tho!
As last step, we need to grant the System Assigned Identity the permission to use Microsoft Graph.

We’re doing this via Powershell, for this we’ll need our Tenant id, the id of the System Assigned identity, the GraphAddId and the name of the Automation Account.

$TenantID="YOUR TENANT"
$GraphAppId = "00000003-0000-0000-c000-000000000000"
$DisplayNameOfMSI="YOUR AUTOMATION ACCOUNT"

$PermissionName = @("DeviceManagementConfiguration.ReadWrite.All",
"DeviceManagementConfiguration.Read.All") 

Connect-AzureAD -TenantId $TenantID

$MSI = (Get-AzureADServicePrincipal -Filter "displayName eq '$DisplayNameOfMSI'")
Start-Sleep -Seconds 10
$GraphServicePrincipal = Get-AzureADServicePrincipal -Filter "appId eq '$GraphAppId'"

foreach ($permission in $PermissionName){

    $AppRole = $GraphServicePrincipal.AppRoles | Where-Object {$_.Value -eq $permission -and $_.AllowedMemberTypes -contains "Application"}
    New-AzureAdServiceAppRoleAssignment -ObjectId $MSI.ObjectId -PrincipalId $MSI.ObjectId -ResourceId $GraphServicePrincipal.ObjectId -Id $AppRole.Id
}
PowerShell

Now you can start your first Test Run!

Leave a Comment

pfSense or OPNsense on Azure?!

I recently gained access to a MPN azure subscription for my dev tenant, the MPN subscription grants me about ~€145 (150$) in Azure credits per month, but Azure standalone without any access to my on premise resources would be kind of boring, right?

So what were my options? A VPN Gateway? A Marketplace NVA? Well, these are pricey and would eat up my precious Azure credits very quickly on their own. PFSense and VyOS themselves are also available trough the Azure Marketplace, but only the subscription model versions. What can somebody do with limited Azure Credits?

pfSense Community Edition and Hyper-V to the Rescue!

Well, my low cost option, at least for now, is the pfSense Community Edition, on the lowest priced Azure Virtual Machine possible, a Standard_B2ats_v2. So the NVA would rougly cost me ~€11 of my credits per month.

But how do I get the VM up there? Well, the first step was obviously to download the latest AMD64 pfSense Community Edition, then I’ve activated the Hyper-V Role on my Windows Device and installed a pfSense on a local VM with a fixed vhd as disk.

The disk type is important, because dynamic vhdx isn’t yet supported by Azure.

I quickly configured my Azure VNET IPs as static inside the VM and also enabled the Serial Terminal option.

In the “Advanced Setup” are the very useful options regarding Serial Communication hidden, this enables Serial Console access trough the Azure Portal.

After the initial configuration was done, I uploaded the vhd via the Azure Storage Explorer to my subscription and created a VM out of the resulting Storage Disk.

In the resulting Network Interfaces, I enabled the IP forwarding option, so these interfaces can route traffic that is intended for other destinations.

For the WAN Interface, I assigned a Public IP and created a NSG that would allow my Public IP to access the Webinterface Port of the pfSense.

It was not working.

Well, I guess i borked something up in the IP configuration, but since I was wise enough to enable the Serial Console, i was able to change the configuration trough the Azure Portal.

The Azure VM Agent

Installing the Azure VM Agent, with my up and running Serial Console was quite trivial, I found a Post at the Netgate Forum, which basically boils down to cloning into the git repo and installing the lastest version from there.

On my VM I was missing not only git but also some libaries for python, even tho the standard python installation was present, nothing the FreeBSD package manager couldn’t fix.

I’m not going into the details of setting up a Site-to-Site IPSec Tunnel between my FortiGate and the pfSense VM here, just let me say, everything is working as expected, even with VTI and BGP configured.

But is it fast?

I really can’t complain, i chose the closest Azure region and via iperf3 tests the Site 2 Site Tunnel maxes out my current connection (250Mbit/50Mbit), for approx ~€11 a month I couldn’t be happier, especially since I now also have the option to configure OpenVPN in the pfSense box for a direct tunnel to my Azure lab environment..

Leave a Comment

Microsoft Entra Private Access

The end of Point-to-Site VPNs as we know them might be already here, with Microsoft now providing not only a Windows, but also a Mac, Android and iOS client for their Entra Private Access, some of them still in Private Preview, and Cloudflare constantly improving their WARP Client, there is no real need anymore for VPN Access for your users.

Microsoft Global Secure Access

Well, this is a two tier topic. Why you might ask? Because Microsoft is fitting two products below the “Global Secure Access” umbrella.
Microsoft Entra Internet Access and Microsoft Entra Private Access.

Microsoft Entra Internet Access

I won’t go too much into details here, but let’s say, with Microsoft Entra Internet Access you’re able to establish a Site-to-Site VPN from your office locations directly to Microsoft and via a provided BGP peer, your office locations will route traffic that is intended for Microsoft services (Exchange, Intune, OneDrive, Sharepoint,…) directly over this tunnel. Microsoft then offers the option to not only inspect the traffic, but also to use Conditional Access to, for example, restrict the collaboration with certain tenants.

Needless to say, that I’ve already tried that out and locked myself out of my customer tenants, it took me a hot minute on a Monday until understood that I wasn’t able to access them because my home network was routing everything Microsoft related direct to Microsoft and associated it with my tenant – working as intended.

Entra Private Access

Entra Private Access on the other hand is, as the name suggests, your users access to all your onpremise resources and if I say all, I almost everything, RDP, SMB, SSH, FTP, … and everything with Conditional Access. YES Conditional Access on an SMB connection. Madness.

But how does it work? How can I use it? Well, currently it’s included in the Entra ID P1 license and the setup is quite simple, because if you already have an Entra ID Application Proxy Connector up and running, guess what, you also have a Entra Private Access Connector. Yes, they’re the same piece of software now.

If you don’t have an Entra ID Application Proxy Connector yet, you can just visit https://entra.microsoft.com/ navigate down to “Global Secure Access (Preview)”, “Connect” and “Connectors”

Connectors

Once there, you need to activate the Private Network Connectors and download the connector service, you can install it on any Windows Server 2016 and upwards.

Private Network connectors

Once you’ve done so, your server will popup on the dashboard as “available”. One thing to note is, that you should make sure that this server can access all network resources that your users might need to access.

Private Network connectors

If you want redundancy, the you should obviously, as with the Application Proxy Connector, install more than one instance and if there are Network differences, you might want to consider grouping connectors.

From there on, you can just navigate to “Global Secure Access (Preview)” > “Applications” > Enterprise Applications. On this screen simpliy click, New application.

Enterprise applications

Here you can configure the needed access, meaning IPs or FQDNs, Ports (22 for ssh, 445 for SMB, 3389 for RDP and so on…)

Global Secure Access Application

These Enterprise Applications are usable in Conditional Access policies.

How does the end user now access my “Application”?

Well, here is one of the two caveats, for Windows, the end user needs an Entra ID joined device, may it be Hybrid (boo!) or not and for Android/iOS? Well, a configuration for the “Microsoft Defender” App is used, therefore it needs to be Intune managed, the second caveat is, that UDP is currently not supported.

On a Entra ID joined device, you can just install the Global Secure Access Client for Windows (e.g. deploy via Intune) and let SSO do the rest.

No more VPN Client, no Firewall rules or account management, may it be via LDAP, SAML or otherwise, just an Entra Global Secure Access Client and Conditional Access.

Leave a Comment

Cross Forest Certificate Enrollment

Well, in the recent days I was faced with a customer that wanted 802.1x authentication across their whole organisation, quite a simple task, but this particular customer had multiple forests due to acquiring companies in other countries.

A migration or consolidation was no option for the customer, so I found an old article for Server 2008R2 and decided to play it out on Server 2022.

The Setup

The first step was to generate a test setup, which was easily done since I already had my homelab domain, so I configured a new VLAN on my Firewall, spun up two Windows Server 2022 VMs and got the “dev” domain up and running.

After the initial setup of the second domain, I needed to establish a transitive trust with Forest-wide authentication, from then on it was configuring time.

So on my existing homelab AD CS PKI I enabled the ENABLELDAPREFERRALS flag, in order for clients in the new domain to be able to request certificates.

certutil -setreg Policy\EditFlags +EDITF_ENABLELDAPREFERRALS
net stop certsvc && net start certsvc
CMD

Then I exported the cert and published it in the dev domains RootCA and NtAuth store.

Note: do this on a machine in the second forest.

certutil -dspublish -f <RootCACertificateFile> RootCA
certutil -dspublish -f <RootCACertificateFile> NtAuthCA
CMD

Note: if you have the Certificate Management RSAT Tools installed, you can also use “pkiview.msc” to add certficiates to the NtAuthCA store.

In case of a 2 Tier PKI deployment, you should also add your Sub CA to the SubCA and NtAuth store:

certutil -dspublish -f <SubCACertificateFile> SubCA
certutil -dspublish -f <SubCACertificateFile> NtAuthCA
CMD

A quick check confirmed, that my Sub and my Root CA certificates are imported to the NtAuth store of the dev domain, this can be done for all mentioned stores.

certutil ‑viewstore "ldap:///CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration,DC=<ForestRootNameSpace>?cACertificate"
CMD
Certificate Stores

As a next step, I added my Homelab PKI to the Cert Publishers AD Group in the dev domain.

Cert Publishers Group

Then I gave the according permissions, in this case the “Domain Computers” should be able to Enroll the Computer certificate.

Certificate Template permissions

So, to distribute the Root certificate and to ensure autoenrollment, I’ve created a simple gpo.

Certificate Auto-Enrollment group policy
Trusted Root Certificates group policy

As a last step, I tested everything out by running the PKISync.ps1, that is provided on the Microsoft Website, this script is basically checking the CN=Services,CN=PublicKeyServices of the sourceforest domain and copying everything over.

This is how Certificate Templates will be written to the PKI-less Forest.

.\PKISync.ps1 -sourceforest homelab.local -targetforest dev.local -f
PowerShell

This should be run everytime changes are made to the Certificate Templates of the source forest, but I will probably let it run as a scheduled task anyways.

The conclusion?

For me, this worked out pretty well, my dev domain devices now receive the needed certificate.

Certificate
1 Comment