Script to Bulk Invite Guest Users to Azure Entra ID

Introduction

Last week, I was on service desk duty. It’s not my favorite field of endeavour, and I’m past my due date for that kind of work. However, doing it once in a while is an eye-opener to how disorganized and chaotic many organizations are. Some requests are ideal to solve by automating them and handing them over to the team to leverage. That is how I wrote a script to bulk invite guest users to Azure Entra ID, as I could not bring myself to click through the same action dozens of times in the portal. You can find the script on my GitHub page https://github.com/WorkingHardInIT/BulkInviteGuestUsersToEntraIdAzure

Script to Bulk Invite Guest Users to Azure Entra ID

📌 Overview

This PowerShell script allows administrators to bulk invite guest users to an Azure Entra ID (formerly Azure Active Directory) tenant using Microsoft Graph. It includes retry logic for connecting to Microsoft Graph, supports both interactive and device code login, and reads user details from a CSV file.

✨ Features

  • Connects to Microsoft Graph securely using MS Graph PowerShell SDK
  • Retry logic with customizable attempt count
  • Supports both interactive and device-based authentication
  • Invites guest users based on a CSV file input
  • Allows optional CC recipients in the invitation email (limited to one due to API constraints)
  • Includes meaningful console output and error handling

📁 Prerequisites


📄 CSV File Format

Create a file named BulkInviteGuestUsersToAzureEntraID.csv in the same folder as the script with the following columns:

emailAddress,displayName,ccRecipients
guest1@example.com,Guest One,manager1@example.com
guest2@example.com,Guest Two,

Note: Only the first ccRecipient will be used due to a known Microsoft Graph API limitation.


🔧 Script Configuration

Open the script and configure the following variables:

$Scopes = "User.Invite.All" # Required scope
$csvFilePath = ".\BulkInviteGuestUsersToAzureEntraID.csv"
$TenantID = "<your-tenant-id-guid-here>" # Replace with your tenant ID
$emailAddresses = $Null # Optional static list of CC recipients

▶️ How to Run

  1. Open PowerShell as Administrator
  2. Install Microsoft Graph module (if not already):Install-Module Microsoft.Graph -Scope CurrentUser
  3. Execute the script:.\InviteGuestsToAzureEntraID.ps1Or specify login type:Test-GraphConnection -TenantID “<tenant-id>” -Scopes $Scopes -UseDeviceLogin

🧠 Function: Test-GraphConnection

This helper function ensures a valid Microsoft Graph session is established:

  • Disconnects any stale Graph sessions
  • Attempts up to $MaxRetries times to connect
  • Verifies that the session is for the specified Tenant ID
  • Supports -UseDeviceLogin switch for non-interactive login (e.g., headless servers)

📬 Inviting Users

The script loops through all entries in the CSV file and sends out personalized invitations using the New-MgInvitation cmdlet.

Each invite includes:

  • Redirect URL (https://mycompany.portal.com)
  • Display name from CSV
  • Custom message
  • Optional CC recipient (only first address is respected by Graph API)

⚠️ Known Issues

  • CC Recipients Limitation: Only the first email in ccRecipients is honored. This is a known issue in the Microsoft Graph API.
  • Multi-user CC: If different users need unique CCs, adapt the script to parse a ccRecipients column with user-specific values.

📤 Example Output

✅ Using device login for Microsoft Graph...
✅ Microsoft Graph is connected to the correct Azure tenant (xxxx-xxxx-xxxx).
✅ Invitation sent to Guest One using guest1@example.com
⚠️  Skipped a user due to missing email address.
⚠️  Failed to invite Guest Two: Insufficient privileges to complete the operation

🧽 Cleanup / Disconnect

Graph sessions are managed per execution. If needed, manually disconnect with:

Disconnect-MgGraph

📚 References


🛡️ License

This script is provided as-is without warranty. Use it at your own risk. Feel free to adapt and extend as needed.


✍️ Author

Didier Van Hoye

Contributions welcome!

Veeam Vanguard 2025

Veeam Vanguard 2025

I have some fantastic news to share. I’ve been awarded Veeam Vanguard status for the 11th consecutive year! Yes, I made Veeam Vanguard 2025.

A decade of community bliss

I have been a proud and satisfied Veeam customer for over a decade. Throughout this journey, I have shared my experiences and insights from the field while using their products. Over the years, the IT landscape has evolved tremendously. While many advancements have made things easier and more efficient, they have also introduced a more diverse, complex, and fragmented environment that requires data protection and management.
From simple file restores to full-scale disaster recoveries across on-premises, hybrid, and cloud environments, Veeam has continuously evolved to meet these challenges with excellence. Today, Veeam offers such a powerful and versatile suite of products that it has become a benchmark in many architectural designs.
We live in an era of uncertainty, rapid change, and shifting landscapes. The need to navigate these challenges has never been more critical. More than ever, the ability to handle both minor incidents and large-scale catastrophes has become the foundation of modern IT strategies. Ransomware, cybercrime, political instability, and economic turmoil have brought data protection to the forefront of discussions with partners and colleagues. The knowledge and expertise I’ve gained by working with Veeam, alongside the brilliant minds in the Veeam 100 community, have empowered me to contribute to these conversations and develop solutions that address today’s evolving threats.

Thank you, Veeam Community

To my fellow Veeam Vanguards, legends, and MVPs, thank you for being part of this incredible journey. A special shoutout to Mike Resseler for initially bringing me into this program. I also want to express my gratitude to Rick Vanover, Nikola Pejková, Madalina Cristil, Safiya Mohamed, Michael Cade, Edwin Weijdema, and Anton Gostev for their trust and support over the years. Their dedication to making Veeam and its community a success is unparalleled.
The support from Veeam’s leadership, including Anton Gostev and many of their employees, truly makes the Veeam community the best in the industry. They not only enable but actively empower the community in every possible way.
Here’s to another year of great conversations, collaboration, innovation, and success!
hashtag#Veeam hashtag#Veeam100 hashtag#VeeamVanguard hashtag#Community

See Veeam 100 Directory | Veeam Community Resource Hub for more information.

How to fix locking yourself out of OPNsense

Introduction

Eventually, we all make the mistake of locking ourselves out of our firewalls. Let’s look at how to fix locking yourself out of OPNsense. Let’s look at how to fix locking yourself out of OPNsense.

How to fix locking yourself out of OPNsense

With OPNsense, this is mainly due to an error in Interface configuration and firewall rules. You know, when we are too “strict” and deny traffic from private networks on the interface we use for management.

How to fix locking yourself out of OPNsense

Cause 1: Firewall rules are blocking you

These can be user-treated rules or the rules added when you select to block private address ranges on an interface.

There is an easy solution, but it requires console access. If OPNsense runs in a virtual machine, that is relatively easy, especially in the lab or when you are the hypervisor administrator. Now, if OPNsense is running on an appliance, you’ll probably need physical access to that device. Bring a keyboard and a monitor with whatever cable (VGA/DVI/HDMI/DisplayPort/USB-C) is required, or connect a physical console cable to connect to the device. This can only be done remotely if the console port is available over ethernet.

Log in with an account with sufficient rights and drop into the shell by selecting option 8.

How to fix locking yourself out of OPNsense

Type:

pfctl -d

Hit “Enter”. This turns the OPNsense device into a router only by disabling the firewall. That means you now have access again via HTTPS or SSH on the interfaces you list for administration despite the error you made in the firewall rules for those interfaces.

Connect via the Web GUI and fix that mistake. When done, turn the firewall back on. To do so type:

pfctl -e

Hit “Enter”. The firewall is now enabled again.

Test whether you still have Web GUI or SSH access. If so, mission accomplished.

Cause 2: You no longer have HTTPS/SSH listening on the interface you have access to

By default, you listen to all non WAN interfaces. You might have reduced this to one or more but accidentally forgot to select the one(s) you need.

No fear, under /conf/conf.xml, you can edit the administrative webgui and ssh settings. In the example below, I have customized those settings (via the WebGUI) to listen to the specified ports.

WebGUI

SSH

How to fix locking yourself out of OPNsense

Add the missing interface(s) or allow the WebGUI and SSH to listen to all of them again by reverting the settings back to default and not specifying any interfaces, as in the example below.

WebGUI

How to fix locking yourself out of OPNsense

SSH

To edit these files, you can use vi, which is available by default. If you prefer Nano or such, you can install it via the FreeBSD package manager:

pkg install nano

Voila, those are the most common ways to get out of a pickle when you have locked yourself out of OPNsense.

Proximus IPTV decoder DHCP Options Reference

Introduction

This blog serves as a Proximus IPTV decoder DHCP Options Reference. It is nothing more than an ICS DHCP .conf file to leverage in OPNsense to help tweak the configuration for Proximus (Fiber, SIngle VLAN 20) IPTV work on an OPNsense appliance (physical or virtual) instead of via the Internet Box. See DHCP — OPNsense documentation.

I still need to put in the lab time to try to convert my config to KEA DHCP, as ICS DHCP is getting a bit old.

Proximus IPTV decoder DHCP Options Reference

This blog post is meant to be a reference document I can return to and add to when needed. Please feel free to add to it or correct info via the comments. I am working on more elaborate documentation explaining how you can use your 3rd party OPNsense Firewall/Router with Porximus (Internet, IPTV, and VOIP) in the single VLAN 20 setup they are now rolling out. The official documentation is a bit too vague in certain areas. Also, with so many devices, Proximus has no commercial interest in supporting them. That said, OPNsense, pfSense, Unifi, OpenWRT, DD-WRT, MicroTik, and others would cover the most popular ones and do miracles to make an ISP/telco loved instead of seen as a necessary evil. With the prices they charge, they should be able to afford and fund that effort.

Later, when “complete,” I’ll also throw this on GitHub.

Custom ICS DHCP config file Proximus Decoders

You’ll need to use your own interfaces (physical or VLAN) subnet, grab the MAC address of your decoder(s), and verify your decoder(s) hardware version (sniff it or grab it from the system Info via your TV). I got the other values for the DHCP options by capturing DHCP traffic from the decoder. Hence, this blog is my Proximus IPTV decoder DHCP Options Reference.

option space ProximusDecoderV5C;
option ProximusDecoderV5C.serviceName code 4 = text;

# This decoder works with Proximus Fiber To The Home and is the one I could test with.
# Please fill out the MAC address of your decoder. The "1" means ethernet and is not part of the MAC address.
class "ProximusDecoderV5C" {
    match if (substring(hardware, 0, 7) = 1:62:de:c8:c8:ff:47 and substring(option vendor-class-identifier, 0, 19) = "IPTV.CISCO.ISB8320E");
}
# Below classes are older or newer decoders and the info I could find about them for this use case.
# You must figure it out with network captures, Wireshark, and DHCP tests.

# This is an older decoder V5 (Mini?) - Obsolete and probably does not work with Proximus Fiber To The Home
# Please fill out the MAC address of your decoder. The "1" means ethernet and is not part of the MAC address.
#I do not have access, so I could not sniff out DHCP Option 43 to find it.
class "ProximusDecoderV5" {
    match if (substring(hardware, 0, 7) = 1:62:de:c8:c8:ff:48 and substring(option vendor-class-identifier, 0, 18) = "IPTV.CISCO.IPV5001");
}

# This is decoder V6- Obsolete and being replaced. Maybe due to it being Huawei? It might or might not work with Proximus Fiber To The Home.
# I do not have access, so I could not sniff out DHCP Option 43 to find it.
# Please fill out the MAC address of your decoder. The "1" means ethernet and is not part of the MAC address.
class "ProximusDecoderV6" {
    match if (substring(hardware, 0, 7) = 1:62:de:c8:c8:ff:49 and substring(option vendor-class-identifier, 0, 19) = "IPTV.HUAWEI.EC6109V1");
}

#This is decoder V7. I have not had one to play with, so I am unsure of the system version. CHECK IT YOURSELF! Works with Proximus Fiber To The Home.
#I do not have access, so I could not sniff out DHCP Option 43 to find it.
#Please fill out the MAC address of your decoder. The "1" means ethernet and is not part of the MAC address.
class "ProximusDecoderV7" {
    match if (substring(hardware, 0, 7) = 1:62:de:c8:c8:ff:50 and substring(option vendor-class-identifier, 0, 19) = "IPTV.TECHNICOLOR.UIW4020PXM");
}


# Anything else you might plug-in like a smart TV directly - optionally you can just refuse to lease it an address to block use by unknown devices>
# Alteratively you can filter on MAC addresses.
class "NotProximusDecoder" {
    match if not (substring(option vendor-class-identifier, 0, 19) = "IPTV.CISCO.ISB8320E");
}

subnet 192.168.210.0 netmask 255.255.255.0 {
    Pool{
        allow members of "ProximusDecoderV5C";
        range 192.168.210.101 192.168.210.111;

        # Route/GW for the subnet of IPTV VLAN
        option routers 192.168.210.1;

        # Subnetmask for the subnet of IPTV VLAN
        option subnet-mask 255.255.255.0;

        # Broadcast address for the subnet of IPTV VLAN
        option broadcast-address 192.168.210.255;
        # Proximus STB/decoder V5c has this VCI (checked with DHCP client tool and Wireshark
        option vendor-class-identifier "IPTV.CISCO.ISB8320E";
        
        # Vendor-specific option space for IPTV
        vendor-option-space ProximusDecoderV5C;
        
        # Proximus defined IPTV specific options
        option ProximusDecoderV5C.serviceName = "RS";
        
        # Bootfile name for the device
        option bootfile-name "CVT/2/239.255.1.218:64010+SA=239.255.1.218:64010+SAP/3/239.192.4.31:9875"; # Option 67
        
        # Proximus NTP servers (Option 42)
        option ntp-servers 81.244.255.82, 81.240.251.109, 81.244.255.77, 81.240.251.105;
        
        # Proximus DNS servers (Option 6)
        option domain-name-servers 195.238.2.22, 195.238.2.21;
        max-lease-time 86400;
    }
    Pool{
        allow members of "NotProximusDecoder";
        range 192.168.210.201 192.168.210.211;

        # Route/GW for the subnet of IPTV VLAN
        option routers 192.168.210.1;

        # Subnetmask for the subnet of IPTV VLAN
        option subnet-mask 255.255.255.0;

        # Broadcast address for the subnet of IPTV VLAN
        option broadcast-address 192.168.210.255;
            
        # Proximus NTP servers (Option 42)
        option ntp-servers 81.244.255.82, 81.240.251.109, 81.244.255.77, 81.240.251.105;
        
        # Proximus DNS servers (Option 6)
        option domain-name-servers 195.238.2.22, 195.238.2.21;
        max-lease-time 86400;
    }
}