# Microsoft Intune

This guide covers deploying Safety Endpoint to Windows devices using the **Remediations** feature in Microsoft Intune. The deployment uses a single detection script that installs, updates, and verifies Safety Endpoint on a recurring schedule.

## Overview

Safety Endpoint is deployed as an Intune Remediation with a **detection script only** — no remediation script is needed. The detection script downloads the official setup script, verifies its Authenticode signature, and executes it. Since the setup script is idempotent, it handles installation, updates, and health checks internally.

Intune reports device status based on the script's exit code:

| Exit Code | Intune Status | Meaning                                     |
| --------- | ------------- | ------------------------------------------- |
| 0         | Compliant     | Safety Endpoint is installed and up to date |
| 1         | Non-Compliant | Setup failed — requires manual review       |

## Prerequisites

{% hint style="info" %}
Before starting, make sure you have:

* **Microsoft Intune** admin access with permissions to create Remediations
* **Enrollment key** from the [Safety Platform](https://getsafety.com/app/device-enrollment) — click **Manage Enrollment Key** → **Create Enrollment Key**
* Target devices running **Windows 10 or later** with PowerShell 5.0+
  {% endhint %}

## Deploy Safety Endpoint

{% stepper %}
{% step %}

#### Get Your Enrollment Key

Log in to the [Safety Platform](https://getsafety.com/app/device-enrollment), click **Manage Enrollment Key** → **Create Enrollment Key**, and copy the generated key. This key links devices to your Safety organization.
{% endstep %}

{% step %}

#### Prepare the Detection Script

Copy the script below and replace `REPLACE_WITH_YOUR_ENROLLMENT_KEY` with your organization's enrollment key:

{% code title="run.ps1" %}

```powershell
# Safety Endpoint - MDM Run Script (Windows)
# Template: values below are populated by the Safety Platform per organization.
# Paste this into your MDM script field (Intune, NinjaOne, etc.)
# Run as: SYSTEM | Schedule: recurring (e.g. every 1 hour)
#
# Downloads the Safety Endpoint setup script, verifies its Authenticode
# signature is from Safety CLI Cybersecurity Inc, and executes it.

# ── Organization Configuration ───────────────────────────────────────
$EnrollmentKey = "REPLACE_WITH_YOUR_ENROLLMENT_KEY"
# Comma-separated list of firewall tools to exclude (e.g. "pip,npm")
$ExcludeFirewallTools = ""
# ─────────────────────────────────────────────────────────────────────

$ErrorActionPreference = "Stop"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

$setupUrl = "https://getsafety.com/cli/setup.ps1"
$tmpFile  = Join-Path $env:TEMP "safety-mdm-setup-$(Get-Random).ps1"

# ── HttpClient with proxy support (same as install.ps1 / setup.ps1) ─
Add-Type -AssemblyName System.Net.Http -ErrorAction Stop

$handler = [System.Net.Http.HttpClientHandler]::new()

$proxyUrl = @($env:HTTPS_PROXY, $env:ALL_PROXY, $env:HTTP_PROXY) |
    Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1

if ($proxyUrl) {
    $proxyUri = [Uri]$proxyUrl
    $proxy = New-Object Net.WebProxy($proxyUri)
    if ($proxyUri.UserInfo) {
        $parts = $proxyUri.UserInfo -split ":", 2
        $proxy.Credentials = New-Object Net.NetworkCredential(
            [Uri]::UnescapeDataString($parts[0]),
            $(if ($parts.Length -gt 1) { [Uri]::UnescapeDataString($parts[1]) } else { "" })
        )
    } else {
        $proxy.UseDefaultCredentials = $true
    }
    $handler.Proxy = $proxy
} else {
    $handler.DefaultProxyCredentials = [Net.CredentialCache]::DefaultCredentials
}

$client = [System.Net.Http.HttpClient]::new($handler)
$null = $client.DefaultRequestHeaders.UserAgent.ParseAdd("Safety-Endpoint/1 (run)")

# ── Download, verify, execute ────────────────────────────────────────
try {
    # 1. Download (preserves exact bytes for Authenticode)
    Write-Host "Downloading $setupUrl ..."
    $response = $client.GetAsync($setupUrl).GetAwaiter().GetResult()
    $null = $response.EnsureSuccessStatusCode()
    $bytes = $response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()
    [System.IO.File]::WriteAllBytes($tmpFile, $bytes)

    # 2. Verify Authenticode signature
    $sig = Get-AuthenticodeSignature -FilePath $tmpFile
    if ($sig.Status -ne "Valid") {
        throw "Signature status: $($sig.Status) - $($sig.StatusMessage)"
    }
    if ($sig.SignerCertificate.Subject -notmatch "O=Safety CLI Cybersecurity Inc") {
        throw "Unexpected signer: $($sig.SignerCertificate.Subject)"
    }
    Write-Host "Signature verified: $($sig.SignerCertificate.Subject)"

    # 3. Execute
    $setupArgs = @("-EnrollmentKey", $EnrollmentKey)
    if ($ExcludeFirewallTools) { $setupArgs += @("-ExcludeFirewallTools", $ExcludeFirewallTools) }
    & $tmpFile @setupArgs
}
catch {
    Write-Error "Safety MDM setup failed: $_"
    exit 1
}
finally {
    Remove-Item $tmpFile -Force -ErrorAction SilentlyContinue
    if ($client) { $client.Dispose() }
}
```

{% endcode %}

{% hint style="warning" %}
Make sure you replace `REPLACE_WITH_YOUR_ENROLLMENT_KEY` with your actual enrollment key before uploading to Intune.
{% endhint %}
{% endstep %}

{% step %}

#### Create the Remediation in Intune

1. In the [Microsoft Intune admin center](https://intune.microsoft.com), go to **Devices** → **Remediations**
2. Click **Create script package**
3. On the **Basics** tab:
   * **Name**: `Safety Endpoint - Setup`
   * **Description**: Installs and maintains Safety Endpoint via signed setup script
4. On the **Settings** tab:
   * **Detection script file**: Upload your configured `run.ps1`
   * **Remediation script file**: Leave empty
   * **Run this script using the logged-on credentials**: **No**
   * **Enforce script signature check**: **No** (the script verifies the setup script's Authenticode signature internally)
   * **Run script in 64-bit PowerShell**: **Yes**
     {% endstep %}

{% step %}

#### Configure the Schedule

On the **Assignments** tab:

1. Click **Add groups** and select the device groups to target
2. Under **Schedule**, set the recurrence:
   * **Recommended**: Once every **1 hour**
   * This ensures new devices get Safety Endpoint quickly and existing devices stay updated

{% hint style="info" %}
The setup script is idempotent. Running it frequently has minimal overhead — it exits quickly when Safety Endpoint is already up to date.
{% endhint %}
{% endstep %}

{% step %}

#### Review and Create

Review the configuration and click **Create** to deploy the remediation.

{% hint style="success" %}
**Quick test:** You can verify the setup works immediately without waiting for the schedule. Navigate to **Devices** → select a target device → click the **three-dot menu (⋯)** → **Run remediation** (Preview). This triggers the detection script on demand so you can confirm it completes successfully.
{% endhint %}
{% endstep %}
{% endstepper %}

## Monitor Compliance

After deployment, monitor your fleet status in Intune:

1. Go to **Devices** → **Remediations**
2. Select **Safety Endpoint - Setup**
3. Review the **Device status** tab

| Status            | What It Means                            | Action                                     |
| ----------------- | ---------------------------------------- | ------------------------------------------ |
| **Compliant**     | Safety Endpoint is installed and current | None                                       |
| **Non-Compliant** | The script failed on this device         | Review the script output in device details |
| **Pending**       | Script has not yet run                   | Wait for the next scheduled run            |

To view the script output for a specific device, click on the device name and check the **Detection output** column for error details.

## Uninstall Safety Endpoint

If you need to remove Safety Endpoint from devices, deploy the uninstall wrapper script as a standalone Intune script. Like the deployment script, it downloads the official uninstall script from Safety, verifies its Authenticode signature, and executes it.

{% hint style="danger" %}
The uninstall script removes **all** Safety Endpoint artifacts from the machine, including configuration, firewall wrappers, package manager settings, and data for all user profiles.
{% endhint %}

{% stepper %}
{% step %}

#### Prepare the Uninstall Script

Copy the script below — no configuration is needed:

{% code title="uninstall-run.ps1" %}

```powershell
# Safety Endpoint - MDM Uninstall Script (Windows)
# Template: paste this into your MDM script field (Intune, NinjaOne, etc.)
# Run as: SYSTEM | Schedule: one-time or on-demand
#
# Downloads the Safety Endpoint uninstall script, verifies its Authenticode
# signature is from Safety CLI Cybersecurity Inc, and executes it.

$ErrorActionPreference = "Stop"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

$uninstallUrl = "https://getsafety.com/cli/uninstall.ps1"
$tmpFile      = Join-Path $env:TEMP "safety-mdm-uninstall-$(Get-Random).ps1"

# ── HttpClient with proxy support (same as install.ps1 / setup.ps1) ─
Add-Type -AssemblyName System.Net.Http -ErrorAction Stop

$handler = [System.Net.Http.HttpClientHandler]::new()

$proxyUrl = @($env:HTTPS_PROXY, $env:ALL_PROXY, $env:HTTP_PROXY) |
    Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1

if ($proxyUrl) {
    $proxyUri = [Uri]$proxyUrl
    $proxy = New-Object Net.WebProxy($proxyUri)
    if ($proxyUri.UserInfo) {
        $parts = $proxyUri.UserInfo -split ":", 2
        $proxy.Credentials = New-Object Net.NetworkCredential(
            [Uri]::UnescapeDataString($parts[0]),
            $(if ($parts.Length -gt 1) { [Uri]::UnescapeDataString($parts[1]) } else { "" })
        )
    } else {
        $proxy.UseDefaultCredentials = $true
    }
    $handler.Proxy = $proxy
} else {
    $handler.DefaultProxyCredentials = [Net.CredentialCache]::DefaultCredentials
}

$client = [System.Net.Http.HttpClient]::new($handler)
$null = $client.DefaultRequestHeaders.UserAgent.ParseAdd("Safety-Endpoint/1 (run)")

# ── Download, verify, execute ────────────────────────────────────────
try {
    # 1. Download (preserves exact bytes for Authenticode)
    Write-Host "Downloading $uninstallUrl ..."
    $response = $client.GetAsync($uninstallUrl).GetAwaiter().GetResult()
    $null = $response.EnsureSuccessStatusCode()
    $bytes = $response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()
    [System.IO.File]::WriteAllBytes($tmpFile, $bytes)

    # 2. Verify Authenticode signature
    $sig = Get-AuthenticodeSignature -FilePath $tmpFile
    if ($sig.Status -ne "Valid") {
        throw "Signature status: $($sig.Status) - $($sig.StatusMessage)"
    }
    if ($sig.SignerCertificate.Subject -notmatch "O=Safety CLI Cybersecurity Inc") {
        throw "Unexpected signer: $($sig.SignerCertificate.Subject)"
    }
    Write-Host "Signature verified: $($sig.SignerCertificate.Subject)"

    # 3. Execute
    & $tmpFile
}
catch {
    Write-Error "Safety MDM uninstall failed: $_"
    exit 1
}
finally {
    Remove-Item $tmpFile -Force -ErrorAction SilentlyContinue
    if ($client) { $client.Dispose() }
}
```

{% endcode %}
{% endstep %}

{% step %}

#### Add the Script in Intune

1. In the Intune admin center, go to **Devices** → **Scripts and remediations** → **Platform scripts**
2. Click **Add** → **Windows 10 and later**
3. On the **Basics** tab:
   * **Name**: `Safety Endpoint - Uninstall`
   * **Description**: Removes all Safety Endpoint artifacts from the device
     {% endstep %}

{% step %}

#### Configure Script Settings

On the **Script settings** tab:

* **Script file**: Upload `uninstall-run.ps1`
* **Run this script using the logged-on credentials**: **No**
* **Enforce script signature check**: **No** (the script verifies the uninstall script's Authenticode signature internally)
* **Run script in 64-bit PowerShell**: **Yes**
  {% endstep %}

{% step %}

#### Assign and Run

1. On the **Assignments** tab, select the device groups to target
2. Click **Review + add** to deploy

The script runs once per device. It requires SYSTEM or Administrator privileges and will clean up:

* Safety Endpoint binaries and system PATH entries
* Per-user configuration, firewall wrappers, and shell profiles
* Package manager configurations (pip, uv, npm)
* Scheduled tasks created by Safety Endpoint
  {% endstep %}
  {% endstepper %}

## Troubleshooting

<details>

<summary>Script fails with proxy or network errors</summary>

The detection script automatically detects proxy settings from the `HTTPS_PROXY`, `ALL_PROXY`, or `HTTP_PROXY` environment variables. If your network requires a proxy that is not configured via these variables, set the appropriate environment variable at the system level:

1. Go to **System Properties** → **Environment Variables**
2. Add a system variable `HTTPS_PROXY` with your proxy URL (e.g., `http://proxy.company.com:8080`)

If using an authenticated proxy, include credentials in the URL: `http://user:password@proxy.company.com:8080`

</details>

<details>

<summary>Signature verification fails</summary>

The detection script verifies that the downloaded setup script is signed by **Safety CLI Cybersecurity Inc** using Authenticode. If signature verification fails:

* Ensure the device has internet access to `getsafety.com`
* Check that TLS 1.2 is enabled on the device
* Verify no network appliance is intercepting or modifying HTTPS traffic (SSL inspection can break Authenticode signatures)

</details>

<details>

<summary>Script shows Non-Compliant but Safety Endpoint is installed</summary>

A Non-Compliant status means the detection script exited with a non-zero code. This can happen if:

* The setup script detected a problem during its health check
* A newer version failed to install
* A temporary network issue occurred during the run

Check the detection output in the device details for the specific error. The next scheduled run will retry automatically.

</details>

<details>

<summary>PowerShell execution policy blocks the script</summary>

When deploying through Intune Remediations, scripts run under the SYSTEM account with execution policy bypass. If you still encounter execution policy issues:

* Ensure **Run script in 64-bit PowerShell** is set to **Yes**
* Verify no Group Policy overrides the execution policy for the SYSTEM account

</details>
