Set Users’ MFA

Set Users’ MFA

It’s time to Set Users’ MFA with a nice little function. This function is designed to gather information and set a user’s MFA with the method you want. It even allows you to reset and disable MFA. We are going to break this blog up a little differently. We will go through each area of the function as the breakdown. Let’s get started.

Function and Parameters

The first thing we are going to build out is the Function and the Parameters. The function command is like a wrapper for a script. This way you can add this to your PowerShell tool kit module.

Function Set-SHDUserMFA { 
    [CmdletBinding()]
        param(
    )
}

Parameters

User Principal Name

The parameters are where people select items that they want. We have a few here. The First parameter is the User Principal Name. This is like an email address. This parameter is a List of strings. Basically, you are allowed to add more than one user name to this field. To make life easier we have set a Parameter set name. The PSN is called UPN. So the end user can either type UPN or UserPrincipalName for this parameter. This parameter is Mandatory.

Function Set-SHDUserMFA { 
    [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true, ParameterSetName = 'UPN', Position = 0),ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,][string[]]$UserPrincipalName,
    )
}

Status

Afterward, The status parameter allows you to set the MFA status that you want. Here we have Enabled, Disable, Enforced, and Reset. The Enabled will enable MFA. Disable of course disables. Enforced will skip the text and go straight to the Apps. Finally, the reset will reset all the MFA token flags. This is how you Set Users’ MFA. This parameter isn’t mandatory.

Function Set-SHDUserMFA { 
    [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true, ParameterSetName = 'UPN', Position = 0)][string[]]$UserPrincipalName,
            [Parameter(Mandatory = $false)][validateset("Enabled", "Disable", "Enforced", "Reset")][string]$Status,
    )
}

Info

Finally, we have the Info switch. This switch tells the script to produce an in-depth MFA report on the user. This parameter is not mandatory. We choose not to make it mandatory because we might not care.

Function Set-SHDUserMFA { 
    [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true, ParameterSetName = 'UPN', Position = 0)][string[]]$UserPrincipalName,
            [Parameter(Mandatory = $false)][validateset("Enabled", "Disable", "Enforced", "Reset")][string]$Status,
            [Parameter()][Switch]$Info
    )
}

Begin

PowerShell Version

Chiefly, I stated that we were targeting one section at a time. Here in the begin, we will check the environment that we are working with. The MSOnline module only runs in PowerShell 5 and not PowerShell 7. Thus we need to check. Thus, we use the PS Version Table variable. This will produce the PowerShell version number. We can test against that and end the script if the user is using PowerShell 7. In the below example, we are using the write-error to let the user know what’s going on.

#Checks PS version
if ($PSVersionTable.psversion.major -ne 5) {
    Write-Error "Your Powershell Version is $($PSVersionTable.psversion.major). Msonline service only works on Powershell 5. Ending."
    end 
}

Module Test

Aftward, we test the MSOnline module. The Get-InstalledModule tests the installation status of MSOnline. Running MSOnline command tests the status of the module load. Afterward, we either connect or error out.

#Checks to see if the module is installed if not, tells the end user to install. 
if (Get-InstalledModule -name MSOnline -ErrorAction SilentlyContinue) {

    #Checks to see if the msolservice if it isn't connected, we connect
    if (-not (Get-MsolDomain -ErrorAction SilentlyContinue)) {
        Connect-MsolService
    } 
} else {
    Write-Error "MSOnline Services are not installed. Please install the MSOnline services with the following command `n Install-module MSOnline `n"
}

Process – Set Users’ MFA

Confirming the User

Next, we start the process section. The process is where most of the meat and potatoes are. Firstly we have the User Principal Name loop. This loop is very important. The parameter we created beforehand has multiple possible inputs. Thus, it’s best to loop through those possible inputs. We are going to call the Internal variable UPN.

foreach ($UPN in $UserPrincipalName) {
    #Do Stuff Yall
}

After that, we will be building out a try/catch to test if the user exists. The try/catch will give us the ability to test the user without blowing up the script. Get-MsolUser is the command we will be using with an error action of “stop”. The catch will contain the error write-out.

#Checks if the user exists, if not, ends the upn and writes an error.
try {
    $User = Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop
} catch {
    Write-Error $_.Exception.Message
}

The Switch

Now that we have the user verified, it’s time to move forward. We start with the switch. The switch is a process of if statements but formatted nicely. In our case, we will be building the switch of the status parameter. If the user selected blah, then we take blah action. Here is the basic structure of a switch. It’s switch and the parameters name. Then each possible answer to that parameter. The status is a “validate set” parameter. This means all the answers are inside the parameter.

switch ($Status) {
    #If enabled then we enable mfa
    'Enabled' {
        #Do Something
    }
    'Enforced' {
        #Do Something
    }
    'Reset' {
        #Do Something
    }
    'Disable' {
        #Do Something
    }
 }

Enabled

The enabled will enable the MFA of a target user. We went over some of this code in a previous blog. Firstly, we want to create a new object. This object is a Microsoft Online Administration object that focuses on Strong authentication requirements. Once we create this object we will assign properties different values. Finally, we use the Set-Msoluser to set the user accordingly.

$sar = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
$sar.RelyingParty = "*"
$sar.State = "Enabled"
Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sar

Enforced

On the other hand, We have enforced. Enforced forces the user to use an authentication app. We will follow the same path as before. Create the object, and set the properties. Then set the object to the user with the set-msoluser command. Notice the sar.state property.

$sar = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
$sar.RelyingParty = "*"
$sar.State = "Enforced"
Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sar

Reset

Before we created an object, this time we only need a command. We are using the Reset-Msolstrongauthenticationmethodbyupn. Don’t even try saying the command without spaces. This is a beast of a command, but it works. What it does is it resets the methods that the user uses to login in. If the user selected an authentication app, this will allow the user to set another one up. Great command if a user lost their phone.

Reset-MsolStrongAuthenticationMethodByUpn -UserPrincipalName $UPN 

Disable

Finally, we have “disable”. Unlike before methods we will be setting the strong authentication requirements to NOTHING! ABSOLUTELY NOTHING! (UHF reference).

Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements @()

This is what the switch block looks like when fully put together.

switch ($Status) {

    #If enabled then we enable mfa
    'Enabled' {
        $sar = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
        $sar.RelyingParty = "*"
        $sar.State = "Enabled"
        Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sar
    }

    #if enforced, we enforce mfa
    'Enforced' {
        $sar = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
        $sar.RelyingParty = "*"
        $sar.State = "Enforced"
        Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sar
    }

    #if reset, resets mfa
    'Reset' { Reset-MsolStrongAuthenticationMethodByUpn -UserPrincipalName $UPN }

     #if disable, it removes mfa requirements
    'Disable' { Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements @() }
}

Info Flag

Grab user Info

Finally, the Info Flag piece. Here we are going to be gathering information about the user. It’s at the end of the command because we want the information after we set the user’s MFA. The first step we take is to grab user information. We do this with the Get-Msoluser command. “But couldn’t we use the previous $user grab?” The answer is no because we want the most up-to-date. We want to reflect on the changes we have made beforehand.

#Grabs the user information
$User = Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop

Confirm Strong Authentication Requirements

Afterward, we want to test this data. The first test is, does the user have strong authentication requirements. “Strongauthenticationrequirements” property is our target. Since the property has data, we place that data into the “MFAState” parameter. However, if it doesn’t, we put “Disabled” into this variable.

#If it has a strong authentication requirement we grab the state, if the state is blank, then we return disabled
if ($User.StrongAuthenticationRequirements) {
    $MFAState = $User.StrongAuthenticationRequirements.State
} else {
    $MFAState = 'Disabled'
}

Find the Default

Next, we want to grab the default method type. We do this with a where-object that searches for “isdefault”. Then we expand the method types.

#grabs the Strong authentication methods
$MethodType = $User.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true } | select-object -ExpandProperty MethodType

Determine the Default

This will produce a string if there is a default. However, it will produce null if there is not. So we test to see if the $MethodType has data. Once we do that we start another switch. I like switches. Inside this switch we are comparing the unique names to real names. This makes life easier and allows non-admin answers they can understand. Here is a list of names and their translations.

  • OneWaySMS = SMS
  • TwoWayVoiceMobile = Phone call
  • PhoneAppOTP = TOTP
  • PhoneAppNotification = Authenticator App

However, it’s none of these, we just say, disabled.

#if the methods are not blank, we find out which one is the right method. and make since of the code. If it is blank, we say disabled. 
if ($MethodType) {
    switch ($MethodType) {
        'OneWaySMS' { $DefaultMethodType = 'SMS' }
        'TwoWayVoiceMobile' { $DefaultMethodType = 'Call' }
        'PhoneAppOTP' { $DefaultMethodType = 'TOTP' }
        'PhoneAppNotification' { $DefaultMethodType = 'Authenticator App' }
    }
} else {
    $DefaultMethodType = 'Disabled'
}

PS Custom Object

Finally, the PS custom object. This is where we return the information the user wants. The PowerShell custom object will contain the following information, the username, display name, the mfa state, and the default method type.

[PSCustomObject]@{
    UserPrincipalName = $User.UserPrincipalName
    DisplayName       = $User.DisplayName
    MFAState          = $MFAState
    DefaultMethodType = $DefaultMethodType
}

The last thing we do is we null out the methodtypes. Sometimes PowerShell 5 will carry over these items in a loop.

#Nulls out the method type variable because we are in a loop. 
$MethodType = $null

Conclusion

When you Set Users’ MFA, you make people mad. However, life gets way more secure. This function is inside my tool belt and it helps me out almost every day. I can push hundreds of users through it quickly. I will leave you with the script itself.

The Script

Function Set-SHDUserMFA {
    <#
    .SYNOPSIS
        Sets the MFA status of a user. 
    .DESCRIPTION
        The script can grab the Status information of MFA of a user. It can set the users mfa to enabled, enforced, disabled, and even reset the mfa of a user. 
    .PARAMETER UserPrincipalName
        A list of strings, the user principal names of the users you wish to preform the actions against. 
    .PARAMETER SetStatus
        The action you wish to preform
            Enabled: Enables MFA
            Disable: Disables MFA
            Reset: Resets MFA
            Enforced: Enforces MFA
    .PARAMETER Info
        Grabs UPN, Displayname, The MFA state, and Default Method Type
    .NOTES
        Name: Set-SHDUserMFA
        Author: therandomadmin
        Version: 1.0
        DateCreated: 8/3/2022
    .LINK
        
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'UPN', Position = 0,ValueFromPipeline = $true,valueFromPipelineByPropertyName = $true,)][string[]]$UserPrincipalName,
        [Parameter(Mandatory = $false)][validateset("Enabled", "Disable", "Enforced", "Reset")][string]$Status,
        [Parameter()][Switch]$Info
    )

    BEGIN {
        #Checks PS version
        if ($PSVersionTable.psversion.major -ne 5) {
            Write-Error "Your Powershell Version is $($PSVersionTable.psversion.major). Msonline service only works on Powershell 5. Ending."
            end 
        }

        #Checks to see if the module is installed if not, tells the end user to install. 
        if (Get-InstalledModule -name MSOnline -ErrorAction SilentlyContinue) {

            #Checks to see if the msolservice if it isn't connected, we connect
            if (-not (Get-MsolDomain -ErrorAction SilentlyContinue)) {
                Connect-MsolService
            } 
        }
        else {
            Write-Error "MSOnline Services are not installed. Please install the MSOnline services with the following command `n Install-module MSOnline `n"
        }
    }
    PROCESS {

        #Starts the UPN loop
        foreach ($UPN in $UserPrincipalName) {
            #Checks if the user exists, if not, ends the upn and writes an error.
            try {
                $User = Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop
            }
            catch {
                Write-Error $_.Exception.Message
            }

            #Checks status
            switch ($Status) {
                    
                #If enabled then we enable mfa
                'Enabled' {
                    $sar = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
                    $sar.RelyingParty = "*"
                    $sar.State = "Enabled"
                    Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sar
                }

                #if enforced, we enforce mfa
                'Enforced' {
                    $sar = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
                    $sar.RelyingParty = "*"
                    $sar.State = "Enforced"
                    Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements $sar
                }

                #if reset, resets mfa
                'Reset' { Reset-MsolStrongAuthenticationMethodByUpn -UserPrincipalName $UPN }

                #if disable, it removes mfa requirements
                'Disable' { Set-MsolUser -UserPrincipalName $UPN -StrongAuthenticationRequirements @() }
            }

            #if you flip the flag for info we grab the info. 
            if ($Info) {

                #Grabs the user information
                $User = Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop

                #If it has a strong authentication requirement we grab the state, if the state is blank, then we return disabled
                if ($User.StrongAuthenticationRequirements) {
                    $MFAState = $User.StrongAuthenticationRequirements.State
                }
                else {
                    $MFAState = 'Disabled'
                }
                    
                #grabs the Strong authentication methods
                $MethodType = $User.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq $true } | select-object -ExpandProperty MethodType

                #if the methods are not blank, we find out which one is the right method. and make since of the code. If it is blank, we say disabled. 
                if ($MethodType) {
                    switch ($MethodType) {
                        'OneWaySMS' { $DefaultMethodType = 'SMS' }
                        'TwoWayVoiceMobile' { $DefaultMethodType = 'Call' }
                        'PhoneAppOTP' { $DefaultMethodType = 'TOTP' }
                        'PhoneAppNotification' { $DefaultMethodType = 'Authenticator App' }
                    }
                }
                else {
                    $DefaultMethodType = 'Disabled'
                }

                #creates a ps object and displays all the information collected
                [PSCustomObject]@{
                    UserPrincipalName = $User.UserPrincipalName
                    DisplayName       = $User.DisplayName
                    MFAState          = $MFAState
                    DefaultMethodType = $DefaultMethodType
                }

                #Nulls out the method type variable because we are in a loop. 
                $MethodType = $null
            }
        }
    }
    END {}
}

More Links:

In the Field – IP and Mac Info

In the Field – IP and Mac Info

Today in the field, we needed to find all of the device’s IP and Mac Info for each network adapter. This was a large undertaking. We were looking for a special network adapter that didn’t show up in our normal reports from our RMM. However, our RMM could push out PowerShell scripts and return the network adapters for us. The information we needed was the network adapter index number. Then we needed the mac address and IP address. Finally, we needed the name of the network device. The stage is set.

Network Adapters

Firstly, we need to grab the network adapters. We are using the Get-NetAdapter command. This command lists all the current network adapters on the device in question.

$Adapters = Get-NetAdapter

The net adapter gives us the name of the adapter, the mac address, and the index we are looking for. Since we placed the net adapters in a workable variable, we can loop this variable. While looping, we are targeting the interface index.

Net IP Address

Secondly, the next piece we need is the IP information from each network adapter. We do this with the Get-NetIPAddress command. This command grabs the IP address of each network adapter, along with the interface Index. This means we can marry the mac address to the IP address. This command produces both the IPv4 and IPv6 address.

$IP = Get-NetIPAddress 

This command produces a large list of useful information. The information we want is the index, ipv4 and ipv6 addresses. Since we have both pieces of the pie, it’s time to marry the two.

IP and Mac Info

As a result of having the above commands information, we can marry these two together. We start with a foreach loop of the adapters.

$Return = foreach ($Adapter in $Adapters) {
    #Do Something
}

We are looping the adapters because it’s the physical side of things. Plus, the IP information directly links to the network adapter. We place the foreach loop information into a variable. This allows us to filter later. Next, is the PS custom object. A PS custom object is the object we are after.

$Return = foreach ($Adapter in $Adapters) {
    [pscustomobject][ordered]@{
        #Information
    }
}

It’s within this object that magic happens. The current adapter will provide the name, interface description, mac address, index, link speed, and status. From there we will grab the IPv5 address by using where-objects. First, we search for the index of the IP command for the adapter index. Then we search for the address family to be IPv4. We repeat this process for IPv6. This is what the loop looks like.

$Return = foreach ($Adapter in $Adapters) {
    [pscustomobject][ordered]@{
        Name           = $Adapter.Name
        Description    = $Adapter.InterfaceDescription
        MacAddress     = $adapter.MacAddress
        InterfaceIndex = $Adapter.Interfaceindex
        LinkSpeed      = $Adapter.LinkSpeed
        Status         = $Adapter.Status
        IPV4           = ($IP | where-object { $_.InterfaceIndex -eq $Adapter.Interfaceindex } | where-object { $_.addressfamily -like "IPv4" }).IPaddress
        IPV6           = ($IP | where-object { $_.InterfaceIndex -eq $Adapter.Interfaceindex } | where-object { $_.addressfamily -like "IPv6" }).IPaddress
    }
}

After that, the loop finishes and we display a return. We sort the object by the link status, this way we see the active ones first.

$Return | Sort-Object -Property Status

The Script – IP and Mac Info


$Adapters = Get-NetAdapter
$IP = Get-NetIPAddress 
$Return = foreach ($Adapter in $Adapters) {
    [pscustomobject][ordered]@{
        Name           = $Adapter.Name
        Description    = $Adapter.InterfaceDescription
        MacAddress     = $adapter.MacAddress
        InterfaceIndex = $Adapter.Interfaceindex
        LinkSpeed      = $Adapter.LinkSpeed
        Status         = $Adapter.Status
        IPV4           = ($IP | where-object { $_.InterfaceIndex -eq $Adapter.Interfaceindex } | where-object { $_.addressfamily -like "IPv4" }).IPaddress
        IPV6           = ($IP | where-object { $_.InterfaceIndex -eq $Adapter.Interfaceindex } | where-object { $_.addressfamily -like "IPv6" }).IPaddress
    }
}
$Return | Sort-Object -Property Status

Conclusion

Even though this case was a fringe case, it still shows the power that Powershell can provide. We were able to grab the IP and Mac Info for each network adapter. From there we were able to generate the report needed. Pull all of the Mac addresses and IP addresses needed to complete the project. Overall, this was a success for us. I hope you are able to use some if not all of this code somewhere.

Azure AD Hardening- Revoke disabled sign-in tokens

Azure AD Hardening- Revoke disabled sign-in tokens

As part of my efforts to harden clients’ azure accounts, We need to remove/refresh all the sign-in tokens from the disabled account. Why? That’s very simple, If you don’t, then any signed-in device still has access. For example, if I terminate a user, I block their sign-in in office 365. The block will take effect within 24 hours. This is why we want to Revoke Disabled Sign-in tokens.

The user can steal data if you don’t remove/refresh the sign-in tokens. By removing/refreshing the sign-in tokens, the outlook can no longer authenticate back with office 365. This means no more new emails. Everything on the computer is still a free game. The user can decode the PST files. I revoke/refresh the tokens with a single command from the Azure AD PowerShell module.

Revoke Disabled Sign-in

Get-AzureADUser | where-object {$_.AccountEnabled -eq $False} | foreach-object {Revoke-AzureADUserAllRefreshToken -ObjectId $_.ObjectId}

The Breakdown

It’s time to break down the “Revoke Disabled Sign-in” script. The first step is to grab all the users from the azure ad. We do this with the Get-AzureADUser command. Next, we parse that information through a where-object. We want to filter out the accountEnabled by the value of false. Now that we have all the disabled users we start a foreach-object loop. Inside our loop, we want to trigger the Revoke-AzureADUserAllRefreshToken command using the Object ID. We are grabbing the previous command’s output using the $_ object. The ObjectID is the object we are pulling out.

Conclusion

At the end of the day, this code snippet is a catch-all for standard processes. Whenever you terminate an employee, it’s always a good idea to revoke the sign-in tokens.

I hope this helps. Please let me know if you have any questions.

Microsoft Safety Scanner Powershell One Liner

Microsoft Safety Scanner Powershell One Liner

A client called in and told me a line of information that made me concerned about security. I ran a webroot scan and wanted to give another level of the scan. I am partial to the Microsoft Safety Scanner. It runs well connectwise backstage. You can read more about the safety scanner here. So, lets look at this oneliner.

if (!(Test-Path "c:\Temp")) { New-Item -Path "c:\" -Name Temp -ItemType Directory }; invoke-webrequest -Uri https://go.microsoft.com/fwlink/?LinkId=212732 -OutFile c:\temp\mss.exe -UseBasicParsing; c:\temp\mss.exe /Q /F:Y

The first part of this little script is to test and create the folder that will hold our file. We are doing this by using test-path. Then if the file doesn’t exist, aka !. Then we create it with the new-item.

if (!(Test-Path "c:\Temp")) { New-Item -Path "c:\" -Name Temp -ItemType Directory }

The next part is we are going to download the Microsoft security scanner from Microsoft directly. The link is the direct download. We use invoke-webrequest to download the file. The -outfile flag is where we will download the file at and its name. In this case, we are going to name it something simple. Mss.exe inside our temp folder. We use the -usebasicparsing because most machines only have PowerShell 5.

invoke-webrequest -Uri https://go.microsoft.com/fwlink/?LinkId=212732 -OutFile c:\temp\mss.exe -UseBasicParsing

Then we run the command needed. We start the command with the path. C:\temp\mss.exe. We want it to be quiet and we want to force it. So we use the /Q to quiet, and /F:Y to force.

c:\temp\mss.exe /Q /F:Y

The system will not prompt for any kind of approval. It will run and delete what it needs to delete. This is a simple, deploy and walk away one-liner. So, add it to your deployment scripts and enjoy scanning with a Microsoft safety scanner.

Dynamic Groups in Azure AD – Windows 10/11

Dynamic Groups in Azure AD – Windows 10/11

Dynamic Groups in Azure AD is something that will change how you work. Inside my azure AD, I want a group for my windows 10 devices and a group for my windows 11 devices. Dynamic Groups allow you to set parameters for the members of that group. For example, if you want only Windows 10 devices, you select the build number. Want only Azure AD joined, add the parameter. So on and so forth.

Getting Started with Dynamic Groups in Azure AD

The first thing we need to do is log into azure. Click Groups on the left-hand side. Then Click all groups. Search for the group you want to make, just in case it’s already been built. Once you confirmed that it’s not the case, click “New Group”.

Here are the settings you will need to use:

  • Group Type: Security
  • Group Name: Windows 10
  • Group Description: All windows 10 machines
  • Membership Type: Dyanmic Device

Now you will need to click the Add Dynamic Query to add a custom query.

From here, we will click the Property and select DeviceOSType The operator is Starts With and Finally, the value will be Windows. We select these options for the first check because we only want windows devices. Macs start with “Mac”, Linux starts with the Version Number or the name of the OS type. While Windows always, and I mean always, Starts with Windows. Windows Server, Windows 10, Windows Vista, Windows 3.0.

Next, we need to click Add Expression. The And/Or needs to be set to And. So we are checking both queries. The property is going to be DeviceOSVersion, and the operator is going to be Start With. The value is important. As we have entered the world of Windows 11. Why is the value so important? It’s because Windows 11 is nothing more than a skin for windows 10 when it comes to the version number. Windows 11’s version number is 10.0.22000. While Windows 10 is 10.0.19… It’s super important for windows 10 groups to use a value of 10.0.1 with Start with. If you want only windows 11, use 10.0.2. I am expecting Windows 12 (currently in development) to be 10.0.3. Once you click the ok, You can check the members by clicking the Members tab on the left side of the Windows 10 group.

Windows 10 – 10.0.1
Windows 11 – 10.0.2

This group’s concept can be taken a few steps further. You can call out the windows version, and make groups accordingly. This will give some clearer auditing. This also will tell you what you need to update. I hope this helps out.

For more information about Dynamic Groups in Azure AD go here.

Install Sentinel One with Intune

Install Sentinel One with Intune

It’s time to Install Sentinel One. In this blog, we are going to go through the process of installing Sentinel One through Intune. We are going to be using the IntuneWinAppUtil program, and the MSI download of Sentinel one that you can obtain from your Sentinel one login portal. I will not go over how to download the msi installer.

File/Folder Structure

The next item you will need is the Microsoft Win32 Content Prep Tool [Link]. Once you have downloaded this file, I suggest creating a file structure as follows

  • Intune
    • Files
    • IntuneWin

After that, Extract the intunewinapputil.exe file to the top level of your file structure, intune. Place the MSI inside the Files location. Then we should be ready to run intunewinapputil.exe. Before you do, I always suggest reading the help by using the /? command line prompt. Here is the command we are going to use to convert our file.

Intune Win App Util

.\IntuneWinAppUtil.exe -c c:\Intune\Files -s SentinelOneInstaller.msi - o c:\Intune\IntuneWin

Afterward, we use the command above to convert our file into a intune installer file. This will give us a large amount of control. Things like detection rules, custom msi inputs and more. Which we will be using.

Intune – Setting up the installer

Next, It’s time to crack open the intune process. Log in to https://endpoint.microsoft.com/ with an account with intune rights.

Once you have logged into the endpoint management system. Click the Apps on the left-hand side of the screen.

Since we are deploying sentinel one to windows machines, under by platform, click the windows icon.

Uploading the Installer

Now we are going to click the add button on the right-hand side of the screen. This will bring up the add dialog box. We are deploying out the final option. Click the Windows App (Win32) option.

You will be brought to an upload page. Here you select the app package file and click the blue button on the right-hand side of the screen. This is where we go to the file we created in the previous steps. We upload the file here. Once it uploads it will populate the information like name and other items. Click ok to move to the app information page.

Setting the rules

Here we can change the name. Add a unique, html, description. Update the publisher, the application version and more. The category we are going to select is computer management. They show this as a featured app in the company portal. You want to check that one. This allows people to download this version of perch. If you want to feel in any of the other information you are welcome to. Once you have the required information, click next to go to the Program page. Where we determine how to install this application.

Adding the Key

On this page, we want to show the install and uninstall commands. This is where research comes into play. If you don’t know the silent install command of your program then that’s a problem. If you don’t know how your program responds to installs and the error codes or success codes it produces. that’s also a problem. You will need to know these things for other applications. Sentinel One is unique as it’s commands is a little different. Here is the install command:

msiexec /i "SentinelInstalle.msi" /q SITE_TOKEN="YourSiteToken"

Keep the uninstall command the same. As the MSI installer of Sentinel One is the app code. The biggest problem with the uninstall command with S1 is that it doesn’t work without prior approval. You have to log into the S1 Portal and approve its uninstall. Keep the default and continue.

Another thing to point out, this kicked my tail, the SITE_TOKEN does not have a /, I repeat NO /.

On this page, we can scan the system to make sure we meet the requirements. So if you know this is a heavy application, you can say to have at least 8GB of ram. You can even have PowerShell scripts that can trigger. A good example would be a user. If user Bob is on this PC don’t install it. The required items are the OS Arch and the minimum os. Which is going to be windows 10 1607 as that is intune’s min. Don’t get this confused with the next item. These are the requirements. Not meeting the requirements will prevent the app from installing. Once you have added what you want, click next.

On this page, we are going to select any dependencies that the application may need. Something unique about Sentinel One is, it will capture all of your custom PowerShell deployments. It’s best to set those as dependencies on this installer. This way they will deploy before S1 does. S1, doesn’t need any additional dependencies from my current knowledge and testing.

Detection Rules

We are going to use a Powershell Script to validate the install. Click on the Rule Format and select Use Custom Detection script. Upload your script file using the blue folder icon.

$A = 0
do {
    try {
        $Services = Get-Service -name SentinelAgent
    }
    catch {
        $Services = $null
    }
    start-sleep -Seconds 30
    $A = $A + 30
} until (($null -ne $Services) -or ($A -ge 300))
if ($null -ne $Services) { Write-Host "Installed" } else { exit }

This script checks for the Sentinel Agent every 30 seconds after installation. Then it increases my timer, by 30. It finally stops at 300 seconds. If services are null, it just exits, and thus a failure is seen in intune. If it is installed, it exits with a string. This tells the system that the installation was successful. What I like about this part is, that if the edit needs to be done, it can be done at this point using PowerShell.

The next screen supersedence is where you can choose what will replace the application. This is a preview feature at the moment and the idea is for upgrades. The final section is the Assignment, this is where you will select a group of devices for S1 to deploy out to.

I hope this is helpful for everyone viewing this page.