Featured image of post Automating SentinelOne Deployment Across Azure VMs

Automating SentinelOne Deployment Across Azure VMs

Step-by-step guide to audit, install, and remediate SentinelOne Azure VM extensions with PowerShell.

This guide walks through how to automate SentinelOne deployment across Azure virtual machines by using the Sentinel_Check_&_Install_v1.ps1 script. The script scans subscriptions, skips excluded workloads, checks existing SentinelOne extensions, optionally repairs failed deployments, resolves secrets from secure runtime sources, and exports a CSV summary for audit and follow-up.

What the script does

At a high level, the script:

  1. Authenticates to Azure with Connect-AzAccount.
  2. Enumerates one or more subscriptions.
  3. Maps each subscription to a SentinelOne site token based on the subscription name.
  4. Filters out excluded regions and VM names.
  5. Checks whether each VM is running.
  6. Detects whether the SentinelOne extension is already installed.
  7. Flags failed extension provisioning states.
  8. Resolves the SentinelOne API key and site tokens from parameters, environment variables, or Azure Key Vault.
  9. Installs or reinstalls the SentinelOne extension where needed.
  10. Writes a CSV report covering every decision the run made.

Before you run it

You need the following in place first:

Requirement Why it matters
Azure PowerShell modules, especially Az.Accounts, Az.Compute, and Az.Resources The script relies on Get-AzSubscription, Get-AzVM, Get-AzVMExtension, Set-AzVMExtension, and related commands.
Rights to read subscriptions and manage VM extensions The script installs and removes Azure VM extensions.
SentinelOne console API key Required by the extension install process and now supplied securely at runtime.
SentinelOne site tokens for each environment The script picks a token by matching subscription names such as prod, preprod, qa, and dev.
A safe test scope Start with a limited subscription list and -WhatIf before broad rollout.
📝 Note

The script no longer requires hard-coded secrets in the file. Supply the SentinelOne API key and site tokens through explicit parameters, environment variables, or Azure Key Vault.

Step 1: Review the parameters

The script exposes operational parameters for VM targeting and secret-resolution parameters for secure execution.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
param(
    [string[]] $SubscriptionIds = @(),
    [string[]] $ExcludedLocations = @('ukwest'),
    [string[]] $ExcludedVMNames = @('ufwduks001v', 'ufwduks002v', 'uavsjbtempuks001'),
  [string] $SentinelOneConsoleAPIKey,
  [string] $ProdSiteToken,
  [string] $PreProdSiteToken,
  [string] $QaSiteToken,
  [string] $DevSiteToken,
  [string] $KeyVaultName,
  [string] $KeyVaultSubscriptionId,
    [switch] $AutoStartVMs,
    [switch] $ReinstallFailedExtensions,
    [string] $CsvPath = ".\SentinelCheck_Results_$((Get-Date).ToString('yyyyMMdd_HHmmss')).csv"
)

Use them like this:

Parameter Purpose
SubscriptionIds Limits the run to specific subscriptions. If empty, the script scans every subscription visible to the signed-in account.
ExcludedLocations Prevents deployment in selected Azure regions.
ExcludedVMNames Skips known exception VMs.
SentinelOneConsoleAPIKey and site token parameters Optional explicit secret inputs for ad hoc runs.
KeyVaultName Enables direct secret lookup from Azure Key Vault.
KeyVaultSubscriptionId Lets the script switch to a different subscription before reading Key Vault secrets.
AutoStartVMs Starts stopped VMs so they can be remediated.
ReinstallFailedExtensions Removes failed SentinelOne extensions and installs a fresh copy.
CsvPath Controls where the execution report is written.

Step 2: Provide secrets securely

The script now resolves secrets in this order:

  1. Explicit parameter values.
  2. Environment variables.
  3. Azure Key Vault secrets.

That means you can use the same script in three common ways without editing the file.

Option A: Environment variables

For automation platforms, environment variables are the simplest integration point:

1
2
3
4
5
$env:SENTINELONE_API_KEY = '<secure value>'
$env:S1_SITE_TOKEN_PROD = '<secure value>'
$env:S1_SITE_TOKEN_PREPROD = '<secure value>'
$env:S1_SITE_TOKEN_QA = '<secure value>'
$env:S1_SITE_TOKEN_DEV = '<secure value>'

The script also accepts alternate names such as SENTINELONE_CONSOLE_API_KEY and SENTINELONE_SITE_TOKEN_PROD, but the shorter names above are the cleanest standard.

Option B: Azure Key Vault

If you want the script to resolve secrets directly from Key Vault, provide the vault name and optionally the subscription that hosts the vault:

1
2
3
4
5
.\Sentinel_Check_&_Install_v1.ps1 \
  -SubscriptionIds '00000000-0000-0000-0000-000000000000' \
  -KeyVaultName 'kv-sentinelone-prod' \
  -KeyVaultSubscriptionId '11111111-1111-1111-1111-111111111111' \
  -WhatIf

By default, the script looks for these Key Vault secret names:

Secret Default name
SentinelOne console API key sentinelone-console-api-key
Production site token sentinelone-site-token-prod
Pre-production site token sentinelone-site-token-preprod
QA site token sentinelone-site-token-qa
Development site token sentinelone-site-token-dev

Option C: Explicit parameters

This is useful for a one-off run, but it is the least desirable option for shared automation because secrets can leak into command history or logs.

Step 3: Confirm the subscription-to-token mapping

The script decides which site token to use by checking the subscription name:

1
2
3
4
5
6
7
8
9
function Get-SentinelSiteTokenForSubscription {
    param([string] $SubscriptionName)
    $name = $SubscriptionName.ToLowerInvariant()
    if ($name -match 'preprod') { return $PreProdSiteToken }
    if ($name -match 'prod')    { return $ProdSiteToken }
    if ($name -match 'qa')      { return $QaSiteToken }
    if ($name -match 'dev')     { return $DevSiteToken }
    return $ProdSiteToken
}

This is simple and effective, but only if your Azure subscription naming standard is consistent. Validate that every targeted subscription name contains one of those environment markers. If not, expand the matching rules before rollout.

Step 4: Run a dry run first

Because the script uses SupportsShouldProcess = $true, you can validate scope with -WhatIf.

Example dry run:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$env:SENTINELONE_API_KEY = '<secure value>'
$env:S1_SITE_TOKEN_PROD = '<secure value>'
$env:S1_SITE_TOKEN_PREPROD = '<secure value>'
$env:S1_SITE_TOKEN_QA = '<secure value>'
$env:S1_SITE_TOKEN_DEV = '<secure value>'

.\Sentinel_Check_&_Install_v1.ps1 \
  -SubscriptionIds '00000000-0000-0000-0000-000000000000' \
  -ExcludedLocations 'ukwest' \
  -ExcludedVMNames 'legacy-vm-01' \
  -WhatIf

During this phase, review:

  1. Which subscriptions are discovered.
  2. How many VMs are excluded.
  3. Which VMs are marked WouldInstall.
  4. Whether any subscriptions default to the wrong site token.

Step 5: Understand the VM checks

For each VM, the script performs four important decisions:

  1. Is the VM in scope after location and name exclusions?
  2. Is the VM currently running?
  3. Does a SentinelOne VM extension already exist?
  4. Is the existing extension healthy or failed?

Stopped VMs are not installed by default. They are logged with status VMStopped, and only started when -AutoStartVMs is provided.

Step 6: Install or repair the extension

When the script finds a VM without a SentinelOne extension, it installs the correct extension based on OS type:

OS Publisher Extension type
Windows SentinelOne.WindowsExtension WindowsExtension
Linux SentinelOne.LinuxExtension LinuxExtension

If an existing SentinelOne extension is present but the provisioning state is failed, the script can remove and reinstall it when -ReinstallFailedExtensions is enabled.

Example live run:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$env:SENTINELONE_API_KEY = '<secure value>'
$env:S1_SITE_TOKEN_PROD = '<secure value>'
$env:S1_SITE_TOKEN_PREPROD = '<secure value>'
$env:S1_SITE_TOKEN_QA = '<secure value>'
$env:S1_SITE_TOKEN_DEV = '<secure value>'

.\Sentinel_Check_&_Install_v1.ps1 \
  -SubscriptionIds '00000000-0000-0000-0000-000000000000' \
  -ReinstallFailedExtensions \
  -AutoStartVMs \
  -CsvPath '.\reports\sentinelone-deployment.csv'

Step 7: Read the output categories

The CSV report is the operational record of the run. Expect these statuses:

Status Meaning
AlreadyInstalled SentinelOne extension already exists and does not show failed provisioning.
ProvisioningFailed Existing extension was detected in a failed state.
Installed SentinelOne was installed during the run.
ReinstalledAfterFailure Failed extension was removed and reinstalled successfully.
WouldInstall Dry-run result produced by -WhatIf.
VMStopped VM was skipped because it was not running.
InspectionFailed Extension inspection could not complete.
InstallFailed or ReinstallFailed Installation or remediation failed and needs follow-up.
SkippedSubscription Subscription processing failed or token mapping could not be resolved.

This report is suitable for service desk handoff, remediation tracking, or import into a workbook.

Step 8: Roll out safely in phases

For a production rollout, use a staged approach:

  1. Run against one non-production subscription with -WhatIf.
  2. Run live in non-production and review the CSV output.
  3. Enable -ReinstallFailedExtensions only after confirming the extension settings are correct.
  4. Expand to production subscriptions in controlled batches.
  5. Keep the exclusion lists under change control so exceptions stay visible.

Step 9: Harden the script before long-term use

The script already has good operational structure, but a few changes would make it safer and easier to maintain:

  1. Keep secrets outside the script and rotate them through Key Vault or pipeline secret stores.
  2. Log to a structured output folder rather than the current working directory.
  3. Make environment mapping explicit in configuration instead of matching on free-text subscription names.
  4. Add transcript logging for change evidence.
  5. Run it from an automation account, pipeline agent, or scheduled management host instead of a workstation.

If you want to turn this from an admin script into a repeatable control, use this pattern:

  1. Store API keys and site tokens in Key Vault.
  2. Trigger the script from Azure DevOps or another automation runner.
  3. Pass SubscriptionIds from pipeline variables.
  4. Publish the CSV as a pipeline artifact.
  5. Review failures and stopped VMs as a separate remediation queue.

Final thoughts

This script is a good foundation for bulk SentinelOne deployment because it combines discovery, filtering, secure secret resolution, installation, remediation, and reporting in one flow. The biggest remaining operational concern is governance around subscription naming, rollout control, and evidence capture rather than secret storage in the script itself. Validate token mapping carefully, keep execution scoped, and use -WhatIf plus phased rollout to move from manual installs to controlled automation.