Using the Flow Designer PowerShell Step

lonesoac01
Giga Guru

Hello all,

 

     I have stumbled upon the PowerShell Step in Flow Designer. https://www.servicenow.com/docs/r/yokohama/build-workflows/workflow-studio/powershell-step-action-de... What I am really struggling with is the connection information.  I do not need to execute on a particular midserver as far as I know, I just need A server to run the below code.

 

The below is my powershell script.

 

param(
    [string]$ResponseBody,
    [string]$EncryptedSecret,
    [string]$ApiSecret = "",
    [switch]$SelfTest
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

function Get-OpenSslKeyIvFromPassphrase {
    param(
        [Parameter(Mandatory = $true)]
        [byte[]]$PassphraseBytes,

        [Parameter(Mandatory = $true)]
        [byte[]]$Salt,

        [int]$KeyLengthBytes = 32,
        [int]$IvLengthBytes = 16
    )

    $md5 = [System.Security.Cryptography.MD5]::Create()
    try {
        $derived = New-Object System.Collections.Generic.List[byte]
        $prev = [byte[]]@()

        while ($derived.Count -lt ($KeyLengthBytes + $IvLengthBytes)) {
            $input = New-Object byte[] ($prev.Length + $PassphraseBytes.Length + $Salt.Length)

            [Array]::Copy($prev, 0, $input, 0, $prev.Length)
            [Array]::Copy($PassphraseBytes, 0, $input, $prev.Length, $PassphraseBytes.Length)
            [Array]::Copy($Salt, 0, $input, $prev.Length + $PassphraseBytes.Length, $Salt.Length)

            $prev = $md5.ComputeHash($input)
            $derived.AddRange($prev)
        }

        $all = $derived.ToArray()

        $key = New-Object byte[] $KeyLengthBytes
        $iv = New-Object byte[] $IvLengthBytes

        [Array]::Copy($all, 0, $key, 0, $KeyLengthBytes)
        [Array]::Copy($all, $KeyLengthBytes, $iv, 0, $IvLengthBytes)

        return @{ Key = $key; IV = $iv }
    }
    finally {
        $md5.Dispose()
    }
}

function Decrypt-CryptoJsAesPassphrase {
    param(
        [Parameter(Mandatory = $true)]
        [string]$CipherTextBase64,

        [Parameter(Mandatory = $true)]
        [string]$Passphrase
    )

    $raw = [Convert]::FromBase64String($CipherTextBase64)
    if ($raw.Length -lt 16) {
        throw "Cipher text too short."
    }

    $header = [System.Text.Encoding]::ASCII.GetString($raw, 0, 8)
    if ($header -ne "Salted__") {
        throw "Unsupported format. Expected OpenSSL salted format that starts with Salted__."
    }

    $salt = New-Object byte[] 8
    [Array]::Copy($raw, 8, $salt, 0, 8)

    $cipherBytes = New-Object byte[] ($raw.Length - 16)
    [Array]::Copy($raw, 16, $cipherBytes, 0, $cipherBytes.Length)

    $passBytes = [System.Text.Encoding]::UTF8.GetBytes($Passphrase)
    $derived = Get-OpenSslKeyIvFromPassphrase -PassphraseBytes $passBytes -Salt $salt

    $aes = [System.Security.Cryptography.Aes]::Create()
    try {
        $aes.Mode = [System.Security.Cryptography.CipherMode]::CBC
        $aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
        $aes.Key = $derived.Key
        $aes.IV = $derived.IV

        $decryptor = $aes.CreateDecryptor()
        try {
            $plainBytes = $decryptor.TransformFinalBlock($cipherBytes, 0, $cipherBytes.Length)
        }
        catch [System.Security.Cryptography.CryptographicException] {
            throw "Decrypt failed (invalid padding). This usually means the encrypted value and ApiSecret do not match, or the value was not encrypted with CryptoJS passphrase mode."
        }

        return [System.Text.Encoding]::UTF8.GetString($plainBytes)
    }
    finally {
        $aes.Dispose()
    }
}

function Encrypt-CryptoJsAesPassphrase {
    param(
        [Parameter(Mandatory = $true)]
        [string]$PlainText,

        [Parameter(Mandatory = $true)]
        [string]$Passphrase
    )

    $salt = New-Object byte[] 8
    [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($salt)

    $passBytes = [System.Text.Encoding]::UTF8.GetBytes($Passphrase)
    $derived = Get-OpenSslKeyIvFromPassphrase -PassphraseBytes $passBytes -Salt $salt

    $aes = [System.Security.Cryptography.Aes]::Create()
    try {
        $aes.Mode = [System.Security.Cryptography.CipherMode]::CBC
        $aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
        $aes.Key = $derived.Key
        $aes.IV = $derived.IV

        $encryptor = $aes.CreateEncryptor()
        $plainBytes = [System.Text.Encoding]::UTF8.GetBytes($PlainText)
        $cipherBytes = $encryptor.TransformFinalBlock($plainBytes, 0, $plainBytes.Length)

        $prefix = [System.Text.Encoding]::ASCII.GetBytes("Salted__")
        $combined = New-Object byte[] ($prefix.Length + $salt.Length + $cipherBytes.Length)

        [Array]::Copy($prefix, 0, $combined, 0, $prefix.Length)
        [Array]::Copy($salt, 0, $combined, $prefix.Length, $salt.Length)
        [Array]::Copy($cipherBytes, 0, $combined, $prefix.Length + $salt.Length, $cipherBytes.Length)

        return [Convert]::ToBase64String($combined)
    }
    finally {
        $aes.Dispose()
    }
}

if ($SelfTest) {
    $expected = "my-test-secret"
    $encrypted = Encrypt-CryptoJsAesPassphrase -PlainText $expected -Passphrase $ApiSecret
    $response = @{ encryptedSecret = $encrypted } | ConvertTo-Json -Compress

    $jsonData = $response | ConvertFrom-Json
    $newencryptedSecret = $jsonData.encryptedSecret
    $newSecret = Decrypt-CryptoJsAesPassphrase -CipherTextBase64 $newencryptedSecret -Passphrase $ApiSecret

    if ($newSecret -ne $expected) {
        throw "Self-test failed: decrypted value does not match expected."
    }

    $result = @{ decryptedSecret = $newSecret; message = "Self-test passed." } | ConvertTo-Json -Compress
    Write-Host $result
    return
}

if ([string]::IsNullOrWhiteSpace($EncryptedSecret)) {
    if ([string]::IsNullOrWhiteSpace($ResponseBody)) {
        throw "Provide -EncryptedSecret directly, -ResponseBody with JSON containing encryptedSecret, or run with -SelfTest."
    }

    $jsonData = $ResponseBody | ConvertFrom-Json
    $newencryptedSecret = $jsonData.encryptedSecret
}
else {
    $newencryptedSecret = $EncryptedSecret
}

$newSecret = Decrypt-CryptoJsAesPassphrase -CipherTextBase64 $newencryptedSecret -Passphrase $ApiSecret

$result = @{ decryptedSecret = $newSecret } | ConvertTo-Json -Compress
Write-Host $result

 

 

2 REPLIES 2

shubhamseth
Giga Sage

@lonesoac01 Yes, you need a MID Server. Use any suitable Windows MID Server as the execution host, secure the secret in credentials, and avoid logging decrypted values.

 

The PowerShell step in Flow Designer does not run directly on the ServiceNow instance. It runs through a MID Server, so you need a Windows MID Server available to execute the script.

You do not need a specific target server unless the script must connect to one. In your case, since the script is only doing local encryption/decryption logic, any healthy Windows MID Server with PowerShell support should be enough.

Key points:

  • Configure a Connection & Credential Alias for the PowerShell step.
  • Select a Windows MID Server or MID Server capability that can execute PowerShell.
  • Ensure the MID Server service account has permission to run PowerShell.
  • Store ApiSecret securely as a credential or flow input, not hardcoded.
  • Pass ResponseBody, EncryptedSecret, and ApiSecret as step inputs.

If you only need computation/decryption and no remote server interaction, the MID Server acts as the execution host.

Also, be careful returning decrypted secrets back into Flow outputs/logs, because Flow Designer execution logs may expose sensitive values.

 

 

Issue resolved? → Mark as Correct


Found value? → Mark as Helpful


Tanushree Maiti
Tera Patron

Hi @lonesoac01 

 

Refer:

PowerShell step 

https://www.youtube.com/watch?v=fuJc__pvVxQ

https://www.youtube.com/watch?v=ggdCADJlcF4

 

Please Accept the solution if it assisted you with your question & Mark this response as Helpful.
Regards
Tanushree Maiti
ServiceNow Technical Architect
LinkedIn: https://www.linkedin.com/in/tanushreemaiti
Unlock the power of automation with ServiceNow! In this comprehensive tutorial, learn how to create and execute PowerShell scripts directly from a MID Server, use Flow Designer to streamline the process, and parse the output to store valuable data in ServiceNow tables. Whether you're new to ...
Hi, This video explain how to create a powershell activity in Integration hub and make use of it in flows and subflows. Article Link :https://community.servicenow.com/community?id=community_article&sys_id=0cbc24601b1614903222ea89bd4bcb96 Please suggest and comment if any issues. Thanks, Ashutosh