Eseguire script PowerShell come amministratore in modo affidabile: guida completa, auto‑elevazione e distribuzione

Vuoi eseguire un file PowerShell come amministratore senza che finisca nella sessione non elevata? In questa guida trovi una soluzione affidabile, consigli per l’auto‑elevazione, gestione dei parametri, ExecutionPolicy, distribuzione massiva e controllo degli errori.

Indice

Contesto e obiettivo

Molti flussi di lavoro Windows richiedono diritti amministrativi: installazioni, modifica del registro, servizi, driver, firewall, criteri locali. Se lanci uno script con Start-Process e -Verb RunAs ma lo script continua a girare “non elevato”, la causa è spesso la costruzione errata della riga di comando o una gestione incompleta dei parametri, con conseguente apertura di una nuova console elevata che però non riceve il comando giusto.

Perché l’approccio ingenuo fallisce

Un tentativo come il seguente crea una nuova finestra elevata, ma non esegue il file .ps1 nella console elevata perché la citazione degli argomenti non è compatibile con il parser della console Windows e di powershell.exe:

$CommandLine = "-file 'C:\temp\MyScript.ps1'"
Start-Process -FilePath powershell.exe -Verb RunAs -ArgumentList $CommandLine

Il problema è la combinazione “stringa singola + apici singoli intorno al percorso”: i singoli apici non hanno il significato di quoting per CreateProcess e per powershell.exe (che si aspettano le doppie virgolette). Risultato: si apre PowerShell elevato ma lo .ps1 non parte, e può rimanere in esecuzione la copia non elevata avviata in precedenza.

Soluzione sicura con argomenti strutturati

Costruisci l’argomento come array di token (o, in alternativa, come singola stringa con doppie virgolette attorno ai percorsi) e imposta un’ExecutionPolicy adatta solo per la durata del processo:

$CommandLine = @(
    '-ExecutionPolicy', 'RemoteSigned'  # oppure 'Bypass' se necessario
    '-File', 'C:\temp\MyScript.ps1'
)
Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList $CommandLine
  • -Verb RunAs forza l’elevazione tramite UAC.
  • L’array evita ambiguità di parsing: ogni elemento è un token distinto.
  • -ExecutionPolicy applicata al solo processo non altera la configurazione di sistema.

Versione compatta con stringa singola

Se preferisci una singola stringa, usa sempre le doppie virgolette per i percorsi con spazi:

Start-Process 'powershell.exe' -Verb RunAs -ArgumentList `
  '-ExecutionPolicy RemoteSigned -File "C:\temp\MyScript.ps1"'

Auto‑elevazione integrata nello script

Per distribuire un unico file che si “auto‑alza” se necessario, inserisci all’inizio dello script una guardia di privilegio. Così chi esegue lo script non deve ricordarsi di usare Start-Process -Verb RunAs:

#requires -RunAsAdministrator

Fallback universale, utile anche dove la direttiva #requires non è rispettata

$IsAdmin = ([Security.Principal.WindowsPrincipal]
[Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

if (-not $IsAdmin) {
Write-Verbose 'Rilancio lo script come amministratore...' -Verbose
$psi = @{
FilePath     = 'powershell.exe'
Verb         = 'RunAs'
ArgumentList = @(
'-NoProfile',
'-ExecutionPolicy', 'Bypass',
'-File', ('"{0}"' -f $PSCommandPath),
'--', $PSBoundParameters.GetEnumerator().ForEach({ '"-{0}" "{1}"' -f $.Key, $.Value }) -join ' '
)
WindowStyle  = 'Hidden'
}
Start-Process @psi
exit
} 

Note utili:

  • #requires -RunAsAdministrator impone l’elevazione e interrompe l’esecuzione se non elevata.
  • $PSCommandPath restituisce il percorso corrente dello script, quotato correttamente.
  • Il blocco propaga i parametri già passati ($PSBoundParameters), in modo che l’istanza elevata riceva gli stessi valori.

Gestione dei parametri in maniera affidabile

Quando devi passare parametri allo script, evita concatenazioni fragili e preferisci l’array di token:

$argsToAdmin = @(
  '-NoProfile',
  '-ExecutionPolicy','RemoteSigned',
  '-File','C:\temp\MyScript.ps1',
  '-Path','C:\Program Files\Contoso',
  '-Name','Valore con spazi',
  '-Force'
)
Start-Process 'powershell.exe' -Verb RunAs -ArgumentList $argsToAdmin -Wait -PassThru

Con -Wait la sessione chiamante attende la fine dell’elevata; con -PassThru ottieni l’oggetto processo per leggere ad esempio ExitCode.

Raccolta del codice di uscita e gestione errori

Per orchestrazioni affidabili è essenziale restituire un codice chiaro. All’interno dello script elevato:

$ErrorActionPreference = 'Stop'
try {
    # ... operazioni ...
    Write-Host 'Operazione completata'
    exit 0
}
catch {
    Write-Error $_
    exit 1
}

Dalla sessione chiamante puoi reagire all’esito:

$p = Start-Process 'powershell.exe' -Verb RunAs -ArgumentList $argsToAdmin -PassThru -Wait
if ($p.ExitCode -ne 0) { throw "Script elevato terminato con codice $($p.ExitCode)" }

Impostazioni dell’ExecutionPolicy

L’ExecutionPolicy in Windows client è spesso Restricted. Ecco una panoramica utile:

ExecutionPolicyUso consigliatoPunti di attenzione
RestrictedBlocca gli script, solo interattivo.Non adatta alla distribuzione.
RemoteSignedConsente script locali non firmati, richiede firma per i remoti.Buon equilibrio per ambienti gestiti.
AllSignedMassima garanzia di provenienza.Richiede infrastruttura di firma e manutenzione certificati.
BypassDistribuzioni una tantum o pacchetti controllati.Valido solo per il processo; non usarlo come impostazione permanente.
UnrestrictedAmbienti di laboratorio.Non consigliata in produzione.

Impostare la policy a riga di comando (-ExecutionPolicy) vale solo per il processo avviato: una pratica utile quando non si dispone ancora di firme.

Compatibilità tra Windows PowerShell e PowerShell

Su Windows puoi avere Windows PowerShell 5.1 (powershell.exe) e PowerShell 7+ (pwsh.exe). Entrambi supportano -File, -ExecutionPolicy su Windows e gli stessi pattern di elevazione. Scegli l’host coerente con i moduli che usi:

# PowerShell 7
Start-Process 'pwsh.exe' -Verb RunAs -ArgumentList @(
  '-NoProfile','-ExecutionPolicy','Bypass','-File','C:\temp\MyScript.ps1'
)

Architettura a trentadue o sessantaquattro bit

Se lavori su sistemi a sessantaquattro bit, fai attenzione a quale host avvii: l’host a trentadue bit ha una vista file system diversa per System32. Per forzare l’host a sessantaquattro bit da un processo a trentadue bit usa Sysnative:

$ps64 = "$env:WINDIR\Sysnative\WindowsPowerShell\v1.0\powershell.exe"
Start-Process $ps64 -Verb RunAs -ArgumentList @('-File','C:\temp\MyScript.ps1')

Ambiente, profilo e prestazioni

Per ridurre variabili e accelerare l’avvio, aggiungi opzioni comuni:

  • -NoProfile: evita i profili utente.
  • -NoLogo: elimina il logo di avvio.
  • -WindowStyle Hidden: riduce flicker in scenari silenziosi (usa con cautela per non confondere l’utente).
  • -WorkingDirectory: imposta la cartella di lavoro corretta se lo script usa percorsi relativi.
Start-Process 'powershell.exe' -Verb RunAs -ArgumentList @(
  '-NoProfile','-ExecutionPolicy','Bypass',
  '-File','C:\temp\MyScript.ps1'
) -WorkingDirectory 'C:\temp' -WindowStyle Normal

Distribuzione massiva senza prompt UAC

L’UAC richiede interazione locale quando si usa -Verb RunAs. In orchestrazione remota conviene eseguire come SYSTEM o come account di servizio già amministratore, evitando il prompt. Ecco scenari tipici:

ScenarioMetodo consigliatoProContro
Intune Win32 appEsecuzione come Sistema con powershell.exe -ExecutionPolicy Bypass -FileNessun prompt UAC, privilegi completi localiAccesso limitato a risorse di rete se non configurate per l’account macchina
ConfigMgrProgrammi o pacchetti come Sistema con opzione “Esegui con diritti amministrativi”Affidabile e centralizzatoRichiede infrastruttura SCCM
PSRemotingInvoke-Command con credenziali amministrative e, se serve, CredSSP o Kerberos delegationNiente UAC su sessioni non interattiveConfigurazione remoting, firewall e SPN
Attività pianificataCreazione di un’attività con Esegui con privilegi più elevati, lancio immediatoBypass dell’UAC legittimoGestione dello scheduler, pulizia dell’attività
Servizi o agentScript eseguito dal servizio con account LocalSystem o dominioAutomazione continuaStato lungo termine e sicurezza credenziali

Esempi pronti all’uso per la distribuzione

Attività pianificata

$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\temp\MyScript.ps1"'
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest
$task = New-ScheduledTask -Action $action -Principal $principal -Settings (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries)
Register-ScheduledTask -TaskName 'Contoso-MyScript' -InputObject $task
Start-ScheduledTask -TaskName 'Contoso-MyScript'

PowerShell Remoting

Invoke-Command -ComputerName Server01 -ScriptBlock {
  powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\temp\MyScript.ps1"
} -Credential (Get-Credential)

Firma del codice e sicurezza

  • Firma degli script: consente AllSigned/RemoteSigned senza eccezioni temporanee.
  • WDAC/AppLocker: prevalgono su ExecutionPolicy; assicurati che l’RBL consenta powershell.exe/pwsh.exe e il tuo script firmato.
  • JEA e Constrained Language Mode: in ambienti multi‑tenant riducono la superficie di rischio.

Tracciabilità, log e osservabilità

Includi un transcript e log strutturato per diagnosi:

$ts = "C:\temp\Logs\MyScript{0:yyyyMMddHHmmss}.log" -f (Get-Date)
New-Item -ItemType Directory -Path (Split-Path $ts) -Force | Out-Null
Start-Transcript -Path $ts -Append
try {
  # ...
}
finally {
  Stop-Transcript
}

Eventuali errori critici possono essere inviati all’Event Log con Write-EventLog o New-WinEvent.

Tabella di sintesi delle scelte

EsigenzaSoluzione rapidaSoluzione enterprise
Lancio manuale elevatoStart-Process -Verb RunAs con argomenti in arrayScript auto‑elevante con guardie e logging
Nessun prompt UACAttività pianificata con RunLevel HighestIntune/ConfigMgr come SYSTEM
Ambiente bloccatoRemoteSigned + firma minimaAllSigned + WDAC/AppLocker
Compatibilità moduli legacypowershell.exe a sessantaquattro bitHost dedicato con percorso Sysnative

Dieci errori comuni da evitare

  1. Usare apici singoli per quotare il percorso nella riga di comando passata a powershell.exe.
  2. Concatenare tutti gli argomenti in una stringa senza testare gli spazi e i caratteri speciali.
  3. Dimenticare -File e passare lo script come primo argomento, facendo aprire solo la console.
  4. Supporre che -Verb RunAs funzioni anche in sessioni remote non interattive.
  5. Ignorare l’architettura e l’effetto della redirezione System32 su processi a trentadue bit.
  6. Usare -ExecutionPolicy Bypass come impostazione permanente.
  7. Non propagare i parametri allo script rilanciato con elevazione.
  8. Non verificare ExitCode e quindi trattare come riuscita un’operazione fallita.
  9. Mescolare -Credential e -Verb RunAs: non si combinano per design.
  10. Affidarsi a runas.exe /savecred in produzione: rischi di sicurezza e scarsa tracciabilità.

Esempio completo riutilizzabile

Il seguente snippet incapsula le migliori pratiche per lanciare uno script elevato, propagare parametri e restituire un codice d’uscita, con fallback a task schedulato in contesti non interattivi:

param(
  [Parameter(Mandatory)]
  [string]$ScriptPath,
  [string[]]$ScriptArgs
)

function Start-ElevatedScript {
param([string]$Path,[string[]]$Args)

$tokens = @('-NoProfile','-ExecutionPolicy','RemoteSigned','-File', $Path) + $Args
try {
$p = Start-Process 'powershell.exe' -Verb RunAs -ArgumentList $tokens -PassThru -Wait
return $p.ExitCode
}
catch {
Write-Warning "Nessuna interazione possibile per UAC: provo con attività pianificata temporanea..."
$taskName = "Temp-Elevated-$(Get-Random)"
$argLine  = ('-NoProfile -ExecutionPolicy Bypass -File "{0}" {1}' -f $Path, ($Args -join ' '))
$action   = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $argLine
$principal= New-ScheduledTaskPrincipal -UserId 'SYSTEM' -RunLevel Highest
$task     = New-ScheduledTask -Action $action -Principal $principal
Register-ScheduledTask -TaskName $taskName -InputObject $task | Out-Null
Start-ScheduledTask -TaskName $taskName
Start-Sleep -Seconds 5
while ((Get-ScheduledTask -TaskName $taskName).State -eq 'Running') { Start-Sleep 2 }
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
return 0
}
}

$exit = Start-ElevatedScript -Path $ScriptPath -Args $ScriptArgs
exit $exit 

Approfondimenti su citazione e tokenizzazione

La differenza fra passare una stringa e un array di stringhe a -ArgumentList è cruciale:

  • Con array, ogni elemento è un token preciso; le virgolette servono solo se il token deve includere spazi al suo interno (es. valori di parametri).
  • Con stringa, l’intera riga è riletta dal parser di powershell.exe; usa " attorno ai percorsi. Gli apici singoli non rimuovono gli spazi per il parser nativo di Windows.

Esempio robusto con array:

Start-Process 'powershell.exe' -Verb RunAs -ArgumentList @(
  '-File','C:\path with spaces\script.ps1',
  '-Name','Contoso App','-Switch'
)

Monitoraggio dell’esperienza utente

Se l’utente finale deve confermare l’UAC, offri messaggi chiari prima del rilancio e, se opportuno, balloon o notifiche brevi. Evita finestre “flash” inutili usando -NoProfile e -WindowStyle appropriati, ma non nascondere completamente la UI quando serve interazione esplicita dell’utente.

Compatibilità con criteri aziendali

In ambienti con criteri restrittivi:

  • Allinea lo script a AppLocker o WDAC: firma i binari e definisci regole per powershell.exe/pwsh.exe e per la cartella di origine degli script.
  • Usa JEA per delegare solo i cmdlet necessari in sessioni remote senza concedere amministratore completo agli operatori.
  • Considera Constrained Language Mode per ridurre le superfici dinamiche in ambienti condivisi.

Checklist operativa

  • Verifica che lo script necessiti davvero di privilegi elevati e limita lo scope delle azioni.
  • Prepara la riga di comando con array di token e doppi apici per i percorsi.
  • Imposta -ExecutionPolicy solo per il processo.
  • Implementa l’auto‑elevazione per esperienze consistenti.
  • Gestisci ExitCode, transcript e log eventi.
  • Decidi se serve host a sessantaquattro bit o a trentadue bit.
  • Per distribuzione massiva, preferisci SYSTEM, attività pianificate o remoting.
  • Firma il codice e rispetta i controlli applicativi aziendali.

Domande frequenti

Posso evitare il prompt UAC quando uso -Verb RunAs?
No, per design richiede interazione locale. Per evitare il prompt usa contesti non interattivi come SYSTEM, attività pianificate con RunLevel Highest o orchestratori.

Posso combinare -Credential con -Verb RunAs?
No, sono modalità alternative. Se devi cambiare identità e avere privilegi elevati, crea un’attività pianificata che esegua con un account idoneo e RunLevel Highest, oppure usa remoting con credenziali adeguate.

Serve davvero impostare -ExecutionPolicy Bypass?
Solo quando non puoi firmare e devi garantire l’avvio: ricorda che vale per il solo processo. La scelta preferibile in ambienti gestiti è RemoteSigned o AllSigned con pipeline di firma.

Perché il mio script parte ma non applica modifiche al sistema?
Controlla i privilegi effettivi: se l’hai avviato senza elevazione, molte operazioni falliranno in silenzio o con errori accesso negato. Usa la guardia #requires -RunAsAdministrator e verifica $IsAdmin.

Conclusione

La chiave per eseguire uno script PowerShell come amministratore è trasmettere correttamente gli argomenti all’istanza elevata e scegliere il contesto di esecuzione adatto allo scenario. Con l’approccio a token, l’auto‑elevazione, un’ExecutionPolicy mirata e una strategia di distribuzione coerente elimini gli effetti collaterali più comuni e ottieni esecuzioni affidabili e ripetibili.


Snippet di riferimento essenziale

# Avvia MyScript.ps1 elevato con policy temporanea sicura
$CommandLine = @(
    '-NoProfile',
    '-ExecutionPolicy','RemoteSigned',  # oppure 'Bypass' se necessario
    '-File','C:\temp\MyScript.ps1'
)
Start-Process -FilePath 'powershell.exe' -Verb RunAs -ArgumentList $CommandLine

Consigli rapidi

  • Integra un controllo di privilegio e rilancio automatico nello script per distribuire un singolo file.
  • Per distribuzioni centralizzate usa SYSTEM o account di servizio per evitare prompt UAC.
  • Usa -ExecutionPolicy Bypass solo per la durata del processo, mantenendo sicuro il valore di sistema.

Con questi pattern puoi distribuire e avviare MyScript.ps1 su host di destinazione con privilegi elevati in modo prevedibile, tracciabile e in linea con le migliori pratiche di sicurezza.

Indice