Home > front end >  run powershell as administrator from python
run powershell as administrator from python

Time:07-29

I have a PowerShell script which look like this:

# Define time for report (default is 1 day)
$startDate = (get-date).AddDays(-10)

# Store successful logon events from security logs with the specified dates and workstation/IP in an array
# foreach ($DC in $DCs){
# $slogonevents = Get-Eventlog -LogName Security -ComputerName $DC.Hostname -after $startDate | where {$_.eventID -eq 4624 }
# }

$slogonevents = Get-Eventlog -LogName Security -after $startDate | where {$_.eventID -eq 4624 }

# Crawl through events; print all logon history with type, date/time, status, account name, computer and IP address if user logged on remotely

  $(foreach ($e in $slogonevents){
    # Logon Successful Events
    # Local (Logon Type 2)
    if (($e.EventID -eq 4624 ) -and ($e.ReplacementStrings[8] -eq 2)){
      write-host "Type: Local Logon`tDate: "$e.TimeGenerated "`tStatus: Success`tUser: "$e.ReplacementStrings[5] "`tWorkstation: "$e.ReplacementStrings[11]
    }
    # Remote (Logon Type 10)
    if (($e.EventID -eq 4624 ) -and ($e.ReplacementStrings[8] -eq 10)){
      write-host "Type: Remote Logon`tDate: "$e.TimeGenerated "`tStatus: Success`tUser: "$e.ReplacementStrings[5] "`tWorkstation: "$e.ReplacementStrings[11] "`tIP Address: "$e.ReplacementStrings[18]
    }
}) *>&1 > D:\Cyber_security\Python\test.txt

I want to run this script from python. this script is saved in my D drive.My python script is:

import subprocess, sys

p = subprocess.Popen(["powershell.exe", 
              "D:\Cyber_security\Python\login.ps1"], 
              stdout=sys.stdout)
p.communicate()

but it doesn't work. I need to run powershell as administrator but I don't know how to do it.

CodePudding user response:

You need to nest powershell.exe calls:

  • An outer call that calls PowerShell's Start-Process cmdlet with -Verb RunAs, which allows running any executable with elevation.

  • Since what you want to run with elevation is a .ps1 script, it must be called via powershell.exe, the Windows PowerShell CLI, as in your own attempt, except that you explicitly need to incorporate a Set-Location call to ensure that the script runs in the same working dir. as the caller (C:\Windows\System32 is the default in Windows PowerShell when Start-Process -Verb RunAs is used).

    • If you don't need this, or if you're using pwsh.exe, the CLI of the cross-platform PowerShell (Core) 7 edition (which now retains the working dir. by default), the inner call can be simplified to:
    • powershell.exe -Args '-noprofile -file D:\Cyber_security\Python\login.ps1'
# See bottom section if you also want to get the exit code of 
# the elevated process.
p = subprocess.Popen(
  [
    "powershell.exe", 
    "-noprofile", "-c",
    r"""
    Start-Process -Verb RunAs -Wait powershell.exe -Args "
      -noprofile -c Set-Location \`"$PWD\`"; & D:\Cyber_security\Python\login.ps1
      "
    """
  ],
  stdout=sys.stdout
)
p.communicate()

Note:

  • Running a process with elevation:

    • involves an interactive UAC confirmation / credentials prompt that cannot be bypassed (unless UAC is turned off, which would be ill-advised)
    • invariably runs in a new window.
    • prevents direct capture of the elevated process' output streams; you'll have to redirect to (temporary) files instead, which you can do with Start-Process'
      -RedirectStandardOutput / -RedirectStandardError parameters.
  • CLI parameters -noprofile and -c were added: -noprofile suppresses loading of PowerShell's profiles, and -c (-Command) explicitly indicates that what follows are PowerShell commands to execute.

  • -Wait was added to the Start-Process call above so as to make the outer powershell.exe call wait for the elevated process to exit before continuing.

  • You don't strictly need powershell.exe Start-Process -Verb RunAs for launching an elevated process, but it is the most convenient option.

    • A Python-based solution is possible, but involves fairly complex use of the WinAPI - see this blog post
    • Note that while you can technically use runas.exe /user:Administrator utility to create an elevated session, doing so (a) only works with precisely that account, i.e. the built-in account named Adminstrator, and that account is often disabled in practice (it is disabled by default).
  • An alternative approach would be to modify the .ps1 file to self-elevate on demand (or use a helper .ps1 file that does that) - see this answer


Variation that also gets the exit code of the elevated process:

If your .ps1 script uses the exit statement in order to deliberately report a (process) exit code that signals success vs. failure and you want to query that exit code, a bit more work is needed:

  • Start-Process -PassThru switch outputs a process-information object representing the newly launched process, whose .ExitCode property reports the process exit code (after termination).

  • Due to how the -c / -Command CLI parameter works, the inner powershell.exe call must use exit $LASTEXITCODE explicitly to relay the script's exit code as the elevated process' exit code.

p = subprocess.Popen(
  [
    "powershell.exe", 
    "-noprofile", "-c",
    r"""
    exit (
      Start-Process -Verb RunAs -PassThru -Wait powershell.exe -Args "
        -noprofile -c Set-Location \`"$PWD\`"; & C:\Users\jdoe\Desktop\pg\pg.ps1; exit `$LASTEXITCODE
      "
    ).ExitCode
    """
  ],
  stdout=sys.stdout
)
p.communicate()

print('Terminated with exit code '   str(p.returncode))
  • Related