2025 – Automating updated Named Locations – like a boss!

Welcome to another blogpost.


This time we have a scenario where we want to automate blocking malicious IP’s by downloading them and importing these into a Named Location Conditional Access policy.

If you are subscribing to a blocklist from a vendor and you run AlwaysOnVPN solution and automatically import this blocklist in your firewall – you’re all god. But if you’re not running AlwaysOnVPN – how can you still make sure that these IP’s are blocked from your users?

We will automate this using Conditional Access and Named Locations feauture in Entra. This means making powershell scripts, certificate-based application in Entra and more!

I will be basing this blog heavily on a community script shared by SYS-IKT – especially Endre Nordeide – and the talented people from HelseCERT. The base-script can be found here; blocklist/m365-single-tenant/Update-NamedLocationsFromHelseCert.ps1 at master · helsecert/blocklist · GitHub.
This also includes references to Alexander Filipins blog about deploying named locations, Ali Tajran on how to create an application in Entra with certificate login, lazyadmin.nl on how to create scheduled tasks and newhelptech.wordpress on how to configure the CA-policy. All references is linked to in the script.

This specific blocklist requires you to subscribe to HelseCERTs blocklist to acquire the necessary API-key – but hopefully this blog also inspires those of you that gets list from other sources.


Our task/to-do list will be the following;

  • Create a locked down server/VM to run the scripts from (make this according to your company policy (I will not show these steps here)
  • Create a non-domain-joined local user without any admin-rights – but give the user a “Log on as a batch job”-privilege on this server only
  • Create a self-signed certificate to use with login to the registered app in Entra (this is important as this needs to be stored locally on the users personal cert store)
  • Create the app-registration with correct privileges, scopes and with certificate-based-authentication
  • Create an “empty” Named Location IP range location we can populate with the script
  • Install Microsoft Graph on the server and check that you can connect
  • Create a config-file with necessary data so the script will work
  • Implement the script to suit our environment
  • Check that the script is working before we make the scheduled task
  • Create the scheduled task

Disclaimer;

  • There will be tasks you perform here that needs local admin privileges for setup – but the user performing the scheduled task does not need any rights.
  • Make sure you have a break-glass account before you create CA-policies so you don’t lock yourself out from your own tenant!
  • If you see anything done here that could be done with even more least privilege – I would be very happy if you shared and I will update the blog accordingly.

Create a non-domain-joined local user without any admin-rights – but give the user a “Run as a batch job”-privilege on this server only
Open Local Users and Groups on the server, I choose to type it because I think it has a fun name lusrmgr.msc


Give it a suitable name and description and make a choice on a password policy that suits your policy and store the pw in a password manager.
If you use a domain-user and the server is a domain joined server – feel free to use GPO to delegate “Log on as a batch job”. If you are using a local user, you can edit the local group policy like this;

 
 
Create a self-signed certificate to use with login to the registered app in Entra (this is important as this needs to be stored locally on the users personal cert store)

On our server, open powershell in elevated mode.
By default self-signed certs is valid for 1 year – I changed that to 5 years here.
$mycert = New-SelfSignedCertificate -DnsName “yourdomainhere” -CertStoreLocation “cert:\LocalMachine\My” -NotAfter (Get-Date).AddYears(5) -KeySpec KeyExchange -FriendlyName “MSGraph Automation”
 
Confirm the certificate and copy the ThumbPrint and paste it into Notepad. You will need it later when connecting to Microsoft Graph.
 
$mycert | Select-Object -Property Subject,Thumbprint,NotBefore,NotAfter
 
Export certificate to .cer file.
 
You will need that .cer file when you upload it to the Azure application, which you will create in the next steps.
 
$mycert | Export-Certificate -FilePath “C:\tmp\MSGraphAutomationCert.cer”
 
Export certificate to .pfx file.
 
You will need that .pfx file when using another machine to connect with Certificate Based Authentication. Copy or send the .pfx file and install it on other machines. Save the password in your password manager.
 
$mycert | Export-PfxCertificate -FilePath “C:\temp\MSGraphAutomationCert.pfx” -Password $(ConvertTo-SecureString -String “YourPasswordHere” -AsPlainText -Force)


Here is a screenshot where I performed all necessary codes.
Now let’s move on to making the application in Entra.

 
Create the app-registration with correct privileges, scopes and with certificate-based-authentication

For this you need to have the necessary role in Entra to create an application. Roles that has this is Global Administrator, Cloud Application Administrator, Application Administrator and Application Developer – choose what is right for your company least privilege policy.
Sign into the azure portal, locate “App registration” and choose “+ New registration” – optionally Sign in to Microsoft Entra admin center, Expand Azure Active Directory, Click on Applications > App registrations and Select New registration.
Choose a suitable name for the application and choose supported account types. In this scenario I will go for single-tenant only.


The application appears, note down the App-ID and tenant-ID – we will need this for the config-file later.


The application requires some permissions to be able to update the Named Location.

  • Click on API permissions > Add a permission
  • Select Microsoft APIs > Microsoft Graph
  • Select Application permissions
  • Search for policy.read
  • Expand Policyand select Policy.Read.All and Policy.ReadWrite.ConditionalAccess
  • Click Add permissions
  • Click on Grant admin consent
  • Click Yes
  • The status shows a green checkmark

Now you could also go for a client-id / client-secret approach for login – but we will use the certificate option with the self-signed certificate we just created.

  • Click on Certificates & secrets
  • Click Certificates > Upload certificate
  • Click on the browse icon and select the self-signed MSGraphAutomationCert.cer file in C:\tmp
  • Add the description MS Graph Automation Cert
  • Click Add
  • The certificate appears in the list

Note: Confirm that it has the same certificate thumbprint as the one you exported in the previous step.

Install Microsoft Graph on the server and check that you can connect

Check out Ali Tajarin’s guide on installing Microsoft Graph in detail here; https://www.alitajran.com/install-microsoft-graph-powershell/

  • Run powershell in elevated prompt
  • Set-ExecutionPolicy RemoteSigned -Force
  • Install-Module PowershellGet -Force (It may take some time to download and install the Microsoft Graph PowerShell module. So give it a couple of minutes.)
  • Install-Module Microsoft.Graph.Beta -AllowClobber -Force

And now we connect – BUT – we will connect using the certificate we created and uploaded!

  1. Start Powershell ISE or Visual Studio code and fill in these variables to connect to MSGraph with Certificate Based Authentication;
  2. $ClientId – Find the application (client) ID for the application we just created
  3. $TenantId – Find the tenant-id on the same page
  4. $CertificateThumbPrint – that’s the certificate thumbprint we noted when uploading the certificate.

If you try to run any commands, be sure the application has the necessary rights for it. For instance, a “Get-MgUser” requires a User.Read.All permission.

We now know that we can connect to our newly registered application with the certificate – let’s move on to create the Named Location instance we want to upload the IP’s to!

Create an “empty” Named Location IP range location we can populate with the script

In portal.azure.com, locate “Security” and under “Manage” locate “Named Locations”

In our scenario we want to upload an IP list – so we choose “IP Ranges location”

Choose a suitable name so you recognize it and add one IP range. (I don’t know why, but you can’t create a completely empty policy. I simply added one of the IP-ranges from the blocklists I was going to import here)

We are closing in on the script now – but before that, we need to configure the config file where we are going to store some of the connection information.

If you wait a minute, go back to the server and run this command to retrieve the ID for the named location – you need that for the config file!

Get-MgIdentityConditionalAccessNamedLocation -All

Create a config-file with necessary data so the script will work

This blog is both a way to inspire you how to do this for several scenarios that suits anyone – but in this example, I will rely on the script that will automate a download of a IP-blocklist and automate the upload of this into the Named Location. To get this to work, you need to be a customer with HelseCERT and subscribe on the service that gives you access to this list. If you do that, you will get an API-key for access.

With all that in place, we can make a simple txt-file called config. The syntax for this (thanks Endre Nordeide at SysIKT!) file should be like this;

$TenantId = *yourtenant-id*

$AppId = *yourapp-id*

$CertificateThumbprint = *yourcertificatethumbprint *

$NBPpass = *the API-passkey from HelseCERT*

$smtpserver = *yoursmtp-server *

$smtpto = *who should receive mail*

$smtpfrom = *who should send the mail*

$NamedLocations = *where to save the blocklist, for instance; C:\source\HelseCertBlockList.csv *

$blocklistdomain = *the domain from where to get the blocklist file*

$NamedLocationId = * The ID for your named location*

 Implement the script to suit our environment

The script I use in this scenario could be found here;

blocklist/m365-single-tenant/Update-NamedLocationsFromHelseCert.ps1 at master · helsecert/blocklist · GitHub

The only thing I changed was the path to the config file on line 79, and I also opted to comment out/remove line 96, 97, 98, 108, 109, 111. These 6 lines are needed to get mailflow going, I didn’t want that.

 Check that the script is working before we make the scheduled task

Before we create our scheduled task, I will run the script and see that it;

  • Downloads the blocklist and creates the .csv needed
  • Connects to the app with certificate-based authentication
  • Imports the IP’s to the Named Location.

Before;

After;

Success – added 851 IP-ranges!!!

Now we want to automate it further, making a scheduled task that does this how often we want it. In our scenario, I want it to be done every hour.

 

Create the scheduled task

Create a new basic task and give it a suitable name and description

Trigger;

Choos the trigger for your task. I will choose “Daily” now and afterwards edit it to be every hour.

Action;

I want it to start a program with the following options;

Program/script: powershell

Arguments; -NoProfile -ExecutionPolicy Bypass -File “C:\source\HelseCERTBlocklist\Update-NamedLocationsFromHelseCERT.ps1”

Start in; “C:\source…”

After saving, I edit it again, choose Edit Trigger and set the repeat task to 1 hour and also “Stop task if it runs longer than 1 hour”

Check if it all works

There is several ways to see that everything is set in motion now. You can check

  • History in scheduled tasks to see that the task is really running
  • Check that the script creates the file in the location you defined in the config-file and that it has values. (The script will also check that the file is not empty so it don’t update Named Locations with an empty list)
  • You can check Azure Monitor log to see that the CA is updated;

AuditLogs

| where ActivityDisplayName == ‘Update policy’

| project

    ActivityDateTime,

    ActivityDisplayName,

    TargetResources[0].displayName,

    InitiatedBy.user.userPrincipalName

  • Check the Sign-In logs for the application (service principal login) and check that it’s successfully log in’s with the certificate

 

Create a policy that blocks sign-ins from our created Named Locations

Finally – there is no point in all this work if we don’t block log ins from these IP’s. So let’s head into Conditional Access and make a policy that does this.

If you haven’t, be sure to check my other posts on Conditional Access and how this works. Before you start – be sure to have a breakglass/emergency account so you don’t lock yourself out!

  1. Sign in to the Microsoft Entra admin center as at least a Conditional Access Administrator.
  2. Browse to Entra ID > Conditional Access > Policies.
  3. Select New policy.
  4. Give your policy a name. In my test-tenant this will be named CA008 – Global – AllApps – AnyPlatforms – BlockListHelseCERT – BLOCK
  5. Under Assignments, select Users or workload identities.
    • Under Include, select All users.
    • Under Exclude, select Users and groups and choose your organization’s emergency access or break-glass accounts.
  6. Under Target resources > Resources (formerly cloud apps) > Include, select All resources (formerly ‘All cloud apps’).
  7. Under Network.
    • Set Configure to Yes
    • Under Include, select Selected networks and locations
      • Select the blocked location we just created.
  1. Click Select.
  2. Under Access controls > select Block Access and click Select.
  1. Confirm your settings and set Enable policy to Report-only.
  2. Select Create to create to enable your policy.

That’s it. We accomplished our goal, and we have now automated a workload that downloads the blocklist, automates the Named Location based on this (every hour) and a Conditional Access policy that blocks it. Great work!

 I can promise you already now that there will be a follow up on this blogpost. Because we have a couple of things on the TODO list. I really hope this encourage you to do something similar. If you see any improvements I will be very happy for a comment or tip and I will update the blog.

I know Endre Nordeide in SYS-IKT is working on both automating URL/domain lists to DefenderForEndpoint sensors – this will work on both HelceCERT, KRIPOS and TorNode-lists, so stay tuned!
I have received a couple of scripts from him I’m looking forward to test very soon. He has also planned a md5 check in the scripts – this will make it possible to check for updates a lot more often as it will only upload if the md5 is changed – brilliant!

Another thing I want to test is how to secure the Service Principal with conditional access policy.

As we saw in the creation of the application – this application has permissions to read and write to Conditional Access. Therefore, it would be a good idea to protect this a little bit. To start, I want this service principal to only be able to sign-in from our trusted location.

 To do this in Conditional Access on a workload Identity/Service Principal you will need a stand-alone license. I don’t have that in my tenant so I can’t show it here – but Christian Frohn has a great write-up on how-to, if you are interested. Check it out here; Securing Service Principals in Microsoft Entra ID with Conditional Access policies – christianfrohn.dk

A big thank you to all who contributed , inspired and shared – that’s what a community is all about!

Until next time!

 

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.