Skip to content

Category: Azure

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