Temporarily Bypassing Applocker for Self Extracting Installers

We've always struggled with installers that create temporary exe files in various locations alongside Applocker. Sometimes we're able to grab a copy of the temporary file and whitelist it so the installation can continue but other times the installer bombs out too quickly and we just can't get the file.

One solution is to whitelist the %OSDRIVE%\USERS\TEMP\APPDATA\LOCAL\TEMP directory but for security that's a terrible idea.

I created a process which I'd like to share that uses Microsoft Power Automation (Flow), Azure Automation, and the Microsoft Graph API to achieve the following:
  1. Admin clicks a Flow button on their phone
  2. The Flow button takes user input in the form of Active Directory username (ex. rellington) and passes it to a webhook to an Azure Automation job
  3. Azure Automation job starts which removes the user from the Applocker enforced Active Directory Security Group (Applocker policy is enforced using Intune)
  4. Azure Automation job forces an Intune sync on all Windows devices to pull the Applocker policy from all devices owned by the user we are wanting to perform the installation on
  5. A 30 minute timer is started to allow the Admin to perform the installation
  6. Azure Automation job adds the user back to Applocker enforced Active Directory Security Group
  7. Azure Automation job forces another Intune sync on all Windows devices to put the Applocker policy back in place on the machine owned by the user who needed the software installed

Here's the Azure Automation Runbook code for the process:

======================================================================

# This script will take input from a Flow in the form of a username and remove that user from the Applocker-Enforce group, wait 30 minutes, then add the user back to the group
# Its purpose is to allow for an installation which creates temporary files to complete

Param(
    [object]$WebhookData
)

$WebhookBody=$WebHookData.RequestBody
$Input = (ConvertFrom-Json -InputObject $WebhookBody)
$userid = $Input.userid
$VerbosePreference = ‘Continue’
function Connect-MSGraphAPI {

# Get the stored credentials from Azure Automation Credential Manager to use in the PowerShell Runbook for MS Graph authentication
$graphClientCreds = Get-AutomationPSCredential -Name 'MSGraphApplocker'
$graphClientPW = $graphClientCreds.GetNetworkCredential().Password
$graphUserCreds = Get-AutomationPSCredential -Name ''
$graphUserPW = $graphUserCreds.GetNetworkCredential().Password

# MS Graph Token Request Body for Azure PowerShell Runbook:
$graphTokenRequestBody = @{
"scope" = "https://graph.microsoft.com/.default";
"grant_type" = "password";
"client_id" = "$($graphClientCreds.UserName)";
"client_secret" = "$graphClientPW";
    "username" = "$($graphUserCreds.UserName)";
"password" = "$graphUserPW";
}

# Define the URI using your own Azure tenant name:
$tenantName = "YOUR AZURE TENANT NAME"
$graphRequestUri = "https://login.microsoftonline.com/$tenantName.onmicrosoft.com/oauth2/v2.0/token"

# Get the current datetime and add one hour to it:
$graphTokenExpirationDate = (Get-Date).AddHours(1)

# Make sure the error variable starts empty:
$GraphAPITokenRequestError = $null

# Send the Post request to Microsoft and receive an OAuth2 token:
$script:GraphAPIAuthResult = (Invoke-RestMethod -Method Post -Uri $graphRequestUri -Body $graphTokenRequestBody -ErrorAction SilentlyContinue -ErrorVariable GraphAPITokenRequestError)

# If there's an error requesting the token, say so, display the error, and break:
if ($GraphAPITokenRequestError) {
    Write-Output "FAILED - Unable to retreive MS Graph API Authentication Token - $($GraphAPITokenRequestError)"
    Break
    }
}
# CHECK AUTH TOKEN STATUS, GET ANOTHER IF CLOSE TO EXPIRATION, SET HEADERS WITH IT IF ALL IS WELL
function Invoke-GraphAPIAuthTokenCheck {
    $currentDateTimePlusTen = (Get-Date).AddMinutes(10)
    if ($script:GraphAPIAuthResult) {
        if (!($currentDateTimePlusTen -le $script:GraphAPIAuthResult.expiration_time)) {
            Connect-MSGraphAPI
            Set-GraphAPIRequestHeader
        } else {
            Set-GraphAPIRequestHeader
        }
    } else {
        Connect-MSGraphAPI
        Invoke-GraphAPIAuthTokenCheck
    }
}

# SET THE HEADER FOR ALL MS GRAPH API REQUESTS
function Set-GraphAPIRequestHeader {
    $script:graphAPIReqHeader = @{
        'Content-Type'='application/json'
        Authorization = "Bearer $($script:GraphAPIAuthResult.access_token)"
        Host = "graph.microsoft.com"
    }
}

function sendmail ([string]$subject, [string]$emailBody) {
    $emailAddress = "you@yourdomain.org"
    $cred = Get-AutomationPSCredential -Name "AzureEmailer"
    Send-MailMessage -to $emailAddress  -From "emailercreds@yourdomain.org" -SmtpServer "smtp.office365.com" -Port 587 -UseSsl -Credential $cred -Subject $subject -Body $emailBody
}

# Call the function
Invoke-GraphAPIAuthTokenCheck

# We have a token so now let's do stuff with it
$headers = $script:graphAPIReqHeader
$graphApiVersion = "v1.0"

# Fire Graph command to remove user from the group

$groupid = "YOUR-APPLOCKER-ENFORCED-GROUP-ID"
Invoke-WebRequest -Uri "https://graph.microsoft.com/v1.0/groups/$groupid/members/$userid/`$ref" -Method "DELETE" -Headers $headers -UseBasicParsing

$emailBody = "User removed from Applocker-Enforce Group"
$subject = "User removed from Applocker-Enforce Group"
sendmail $subject $emailBody

# Sleep for 5 minutes then sync all Windows devices with Intune
Start-Sleep -Seconds 300

# Now let's sync
$DCP_resource = "devicemanagement/manageddevices`?`$filter=operatingSystem eq 'Windows'"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)"
$devices = (Invoke-WebRequest -Uri "$uri" -Method "GET" -Headers $headers -UseBasicParsing -ErrorAction Stop | convertfrom-Json).value
foreach ($device in $devices) {
    $deviceid = $device.id
    Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$deviceid')/syncDevice" -Method "POST" -Headers $headers -UseBasicParsing
}

# Sleep for 30 minutes to let installers complete. An hour used to be used but the token would expire and fail to add member back to group.
Start-Sleep -Seconds 1800

# Fire Graph command to add user back to group
$body = @{"@odata.id" = "https://graph.microsoft.com/beta/directoryObjects/$userid"}
$jsonbody = $body | convertto-json
Invoke-WebRequest -Uri "https://graph.microsoft.com/v1.0/groups/$groupid/members/`$ref" -Method "POST" -Headers $headers -UseBasicParsing -Body $jsonbody

$emailBody = "User added back to Applocker-Enforce Group"
$subject = "User added back to Applocker-Enforce Group"
sendmail $subject $emailBody

# Sleep for 5 minutes then sync all Windows devices with Intune
Start-Sleep -Seconds 300
# Now let's sync
$DCP_resource = "devicemanagement/manageddevices`?`$filter=operatingSystem eq 'Windows'"
$uri = "https://graph.microsoft.com/$graphApiVersion/$($DCP_resource)"
$devices = (Invoke-WebRequest -Uri "$uri" -Method "GET" -Headers $headers -UseBasicParsing -ErrorAction Stop | convertfrom-Json).value
foreach ($device in $devices) {
    $deviceid = $device.id
    Invoke-WebRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$deviceid')/syncDevice" -Method "POST" -Headers $headers -UseBasicParsing
}
======================================================================

As for the Power Automation Flow button, it's pretty simple. It just takes in an Active Directory username, looks up the ID of the user, and passes it to the webhook which fires the Azure Automation job off. Here's a visual of what the Power Automation Flow process looks like:


Comments

Popular posts from this blog

Auto-installing extensions on Firefox using Intune

Disable DNS over HTTPS in Firefox using Intune

Moving Applocker control from Group Policy to Intune