I am running a Python FastAPI app with 4 different APIs. In the first one, I have implemented an error handling which after some trouble is working somehow fine. So I copied it's structure to the second API and suddenly it throws an error and won't work anymore.
All of my 4 APIs call a PowerShell script because it's easy to work with the Active Directory with PowerShell. After the first ps1 script is called, it calls another ps1 script under a priviledged user so that we get the permission to add groups to a specific OU (organizational unit in AD). So it's like this: Python app -> First ps1 -> 2nd ps1
My problem: Although there is an error at the 2nd script, I only get zero as an exit code / return code within the Python app and the 1st ps1 script.
I learnt that there is an issue within PowerShell encoding: Python - Get command output cannot be decoded So that's why I am trying to call the first ps script with utf-8 encoding. But I am failing still to trigger the 2nd PowerShell script with utf-8 as well.
To test the error handling, I purposefully implemented a division by zero. Within 2nd script:
Start-Transcript -Append .\managead\ADMgmtlog.txt
Try {
  #Add-ADGroupMember -Identity $groupname -Members $techuser;
  Write-Host "PS1 script ADgroupmgmt: let's crash the script "
  $result = 10/0
} catch {
  $errorMessage = $_.Exception.Message
  Write-Host "PS1 script ADgroupmgmt: Error: " + $errorMessage
  Write-Host "PS1 script ADgroupmgmt: The AD Management script has failed"
  $ErrorActionPreference = 'Stop'
  Write-Host "PS1 script ADgroupmgmt: before throw "
  throw $errorMessage
  Write-Host "PS1 script ADgroupmgmt: after throw"
  Stop-Transcript
}  
Then, I within the first ps script, I call this 2nd one and try to catch the error:
try {            
              
           #$enc = [System.Text.Encoding]::UTF8 #not used currently
           $process = Start-Process -NoNewWindow pwsh.exe -Credential $credential -ArgumentList $ArgumentList #-PassThru
           #do{sleep 1}while(Get-Process -Id $process.Id -Ea SilentlyContinue)
           Write-Host "PS1 script ADMgmtInitial: process: " + $process
           #Write-Host "PS1 script ADMgmtInitial: Now lets decode with GetBytes"
           #$encprocess= $enc.GetBytes($process)
           #Write-Host "PS1 script ADMgmtInitial: encprocess: " + $encprocess
           #$exitCode = $encprocess.ExitCode 
           
           $exitCode = $process.ExitCode  
            
           Write-Host "PS1 script ADMgmtInitial: exitCode: " + $exitCode
           Write-Host "PS1 script ADMgmtInitial: ps1 Request success | action = ${action}; groupname = ${groupname}; tech user = ${techuser}; region = ${region}"
           
        } catch {
           <#
           $encprocess= $enc.GetBytes($process)
           Write-Host "PS1 script ADMgmtInitial: encprocess: " + $encprocess
           $exitCode = $encprocess.ExitCode  
           #>
           
           $exitCode = $process.ExitCode  
           Write-Host "PS1 script ADMgmtInitial: exitCode: " + $exitCode
           
           $errorMessage = $_.Exception.Message
           Write-Host "PS1 script ADMgmtInitial: Error: " + $errorMessage
           Write-Host "PS1 script ADMgmtInitial: The AD Management script has failed"
           throw $errorMessage
           $ErrorActionPreference = 'Stop'
        }
By throwing another error within this catch command, the errorMessage should be passed to the Python app. Here, I am triggering the first ps script like this:
result = subprocess.run(
    f'''\
    chcp 65001 >NUL & pwsh.exe -File "./managead/ADMgmtInitial.ps1" -action "{item.action}" -groupname "{providedadgroup}" -techuser "{item.techuser}" -region "{item.region}"
    ''',
    shell=True,              # call via cmd.exe (on Windows)
    text=True,               # decode output as text
    stdout=subprocess.PIPE,  # capture stdout instead of printing to the console
    stderr=subprocess.PIPE   # capture stderr
)
# Print stdout and stderr output for diagnostic purposes.
print("stdout: " + result.stdout)
print("stderr: " + result.stderr)
returncodestring = str(result.returncode)     
print ("returncode in Python: " + returncodestring)
print("AD group changed | Action= " + item.action + "; csname = " + item.csname + "; Tech User = " + item.techuser + "; Region = " + item.region)
#check error code
if result.returncode == 0: #0 stands for no returned error; everything was executed without errors    
    
    stdoutmessage = result.stderr
    print ("stdoutmessage: ")
    print (stdoutmessage)
    #return specific messages depended on the action type add or remove
    if item.action == "add":
        print("User " + item.techuser + " has been added to the AD group " + providedadgroup)
        return {"message": "Success: User " + item.techuser + " has been added to the AD group " + providedadgroup}
    else: #action == "remove"
        print("User " + item.techuser + " has been removed from the AD group " + providedadgroup)
        return {"message": "Success: User " + item.techuser + " has been removed from the AD group " + providedadgroup}
    
else: # !=0 stands for an error; something went wrong
    returncodestring = str(result.returncode)
    print ("returncode in Python: " + returncodestring)
    errormessagestring= result.stderr.strip()
    print ("Python Error | errormessagestring: " + errormessagestring)
    #return specific messages depended on the action type add or remove
    if item.action == "add":
        print("Error: User " + item.techuser + " could not be added to the AD group " + providedadgroup)
        raise HTTPException(
            status_code=500,
            detail="Failed to add user " + item.techuser + " to the AD group " + providedadgroup +". Details: " + errormessagestring,
            headers={"Error": "Could not add user " + item.techuser + " to the AD group " + providedadgroup},
        )
    else: #action == "remove"
        print("Error: User " + item.techuser + " could not be removed from the AD group " + providedadgroup)
        raise HTTPException(
            status_code=500,
            detail="Failed to remove user " + item.techuser + " from the AD group " + providedadgroup +". Details: " + errormessagestring,
            headers={"Failed to remove user " + item.techuser + " from the AD group " + providedadgroup},
        )
Now, although there is an error thrown, I get exit code == 0 within the first ps script. This code is passed to the Python app where the result.returncode is zero as well. It's really weird because the error is actually regonized when I print it. This is the part of my console:
Action successful: E415_myOU_ as a prefix has been added. Result: E415_myOU_mygroup
stdout: Transcript started, output file is .\managead\ADInitiallog.txt
PS1 script ADMgmtInitial: ps1 Request received | action = add; groupname = E415_myOU_mygroup;         techuser = myuserid; region = EMEA
PS1 script ADMgmtInitial: ArgumentList :-noprofile -file ".\managead\ADgroupmgmt.ps1" -action add -groupname E415_myOU_mygroup -techuser myuserid -region EMEA
PS1 script ADMgmtInitial: Execution started for region EMEA
PS1 script ADMgmtInitial: Calling the ADgroupmgmt.ps1 script to add myuserid to the group     E415_myOU_mygroup
PS1 script ADMgmtInitial: AD Group E415_myOU_mygroup is available:     CN=E415_myOU_mygroup,OU=Groups,OU=myOU,OU=notshown,DC=emea,DC=notshown,DC=net
PS1 script ADMgmtInitial: techuser is a user. Trying to find the user.
PS1 script ADMgmtInitial: AD user myuserid is available: CN=myname\, myname(123),OU=Users,OU=_GlobalResources,OU=notshown,DC=emea,DC=notshown,DC=net
PS1 script ADMgmtInitial: process:  +
PS1 script ADMgmtInitial: exitCode:  +
PS1 script ADMgmtInitial: ps1 Request success | action = add; groupname = E415_myOU_mygroup;     tech user = myuserid; region = EMEA
Transcript stopped, output file is .\managead\ADInitiallog.txt
Transcript started, output file is .\managead\ADMgmtlog.txt
PS1 script ADgroupmgmt: ps1 Request received | action = add; groupname = E415_myOU_mygroup; techuser = myuserid; region = EMEA
PS1 script ADgroupmgmt: ps1 Request | Starting to add myuserid to E415_myOU_mygroup
PS1 script ADgroupmgmt: let's crash the script
PS1 script ADgroupmgmt: Error:  + Attempted to divide by zero.
PS1 script ADgroupmgmt: The AD Management script has failed
PS1 script ADgroupmgmt: before throw
stderr: The specified drive root "C:\Users\myuserid\AppData\Local\Temp\" either does not exist, or it is not a folder. #comment added afterwards: no idea why this error shows up
Exception: .\managead\ADgroupmgmt.ps1:41:7
Line |
  41 |        throw $errorMessage
     |        ~~~~~~~~~~~~~~~~~~~
     | Attempted to divide by zero.
returncode in Python: 0
AD group changed | Action= add; csname = sourcename; Tech User = myuserid; Region = EMEA
stdoutmessage:
The specified drive root "C:\Users\myuserid\AppData\Local\Temp\" either does not exist, or it is not a folder. 
Exception: .\managead\ADgroupmgmt.ps1:41:7
Line |
  41 |        throw $errorMessage
     |        ~~~~~~~~~~~~~~~~~~~
     | Attempted to divide by zero.
User myuserid has been added to the AD group E415_myOU_mygroup #should not be shown because if in-built error
INFO:     127.0.0.1:55917 - "POST /managead HTTP/1.1" 200 OK
So, the API should never return http code 200! I need to prevent this and return the correct 400 or 500 http code.
Do you have any idea why I am not getting the correct exit codes?