Menu Close

PowerShell REST Service

Hi folks,

In a previous post I showed off a little service I had written in PowerShell to wrap Automic ARA’s ARATools.jar methods in a REST service with a neat page that allows you to test POST json data without writing any code. I have bundled these files for download and copied the source code below. Please let me know if you have any questions!

ForestAIR

param (
    [Parameter(Mandatory=$false,Position=0)]$PathToConfig="C:\Users\Rex\Desktop\ForestAIR\config.xml"
)

Function Write-Log { 
    param ( 
        [Parameter(Mandatory=$true,Position=0)]$Text,
        [Parameter(Mandatory=$false,Position=1)][bool]$Error=$False
    )
    $TimeStamp = Get-Date -Format "dd-MM-yyyy HH-mm-ss"
    $LogFile = "$(Get-Date -Format dd-MM-yyyy)_$($Env:COMPUTERNAME).txt"
    $LogFilePath = Join-Path $AIR_LogDirectory $LogFile
    Try { 
        If($Error) { $Color="Red" } Else { $Color = "Green" } 
        Write-Host "$TimeStamp | $Text " -ForegroundColor $Color
        Write-Output "$TimeStamp | $Text " | Out-File $LogFilePath -Encoding UTF8 -Append -Force
    } Catch {
        Write-Output "Failed to write log: $($_.exception.message)"
    }
}

$Global:StartTime = Get-Date -Format "dd-MM-yyyy HH-mm-ss"

########################################################
# PHASE 1: LOAD CONFIGURATION 
########################################################
Try { 
    $ConfigFile = Get-Content -Path $PathToConfig
    $Config = ([xml]$ConfigFile).Config

    # AIR Specific Configuration Items
    $AIR_Port = $Config.AIR.Port
    $AIR_SvcAccount = $Config.AIR.SvcAccount
    $Global:AIR_LogDirectory = $Config.AIR.LogDirectory
    $AIR_SiteRoot = $Config.AIR.SiteRoot

    # ARA Related Configuration Itemss
    $ARA_URI = $Config.ARA.URI
    $ARA_Tools = $Config.ARA.Tools
    $ARA_SvcAccName = $Config.ARA.SvcAccount
    $ARA_SvcAccPass = $Config.ARA.SvcAccountPassword
    Write-Log "######################### AIR SERVICE STARTED #########################"
    Write-Log "AIR Agent started with config: $PathToConfig"
    ForEach ($Line in $ConfigFile) { 
        Write-Log $Line
    }
} Catch { 
    Write-Log "Failed to configure agent: $($_.exception.message)"
    Exit 1
}

########################################################
# PHASE 2: CONFIGURE & START LISTENER
########################################################
Try { 
    # Try to register URLACL for AIR agent webservice under specified service account
    netsh http add urlacl url=http://+:$AIR_Port/ user=$AIR_SvcAccount
    # Create and start HTTP Listener
    $HttpListener = New-Object System.Net.HttpListener
    $HttpListener.Prefixes.Add("http://+:$AIR_Port/")
    $HttpListener.Start()
} Catch { 
    Write-Log "Failed to configure or start listener: $($_.exception.message)"
    Exit 1
}


########################################################
# PHASE 3: WEB SERVICE LOOP
########################################################

While ($HttpListener.isListening) { 
    # Main webservice loop.. processes calls in FIFO fashion. 
    $Context = $HttpListener.GetContext() # GetContext() waits till web request is received 
    $Request = $Context.Request
    $Response = $Context.Response
    $Message = $null
    If($Request.Url.Segments.count -gt 1) { 
        Switch  -Wildcard ($Request.Url.Segments[1]) {
            "listen*" { 
                # Not worrying about this yet
            }
            "create*" {
                Switch -Wildcard ($request.url.segments[2]) { 
                    "package*" { 
                        If ($Request.HttpMethod -eq "GET") { 
                            $Response.ContentType = 'text/html'
                            $message = [System.IO.File]::ReadAllText($(Join-Path $AIR_SiteRoot 'create/package.htm'))
                        } ElseIf ($Request.HttpMethod -eq "POST") { 
                            
                            $StreamReader = New-Object System.IO.StreamReader $request.InputStream
                            
                            $StreamData = $StreamReader.ReadToEnd()
                            If ($StreamData.StartsWith("json_demo=")){
                                $StreamData = $StreamData.Substring(10)
                            } 
                            $JSON = $StreamData | ConvertFrom-Json
                            $ToolsOutput = java -jar $ARA_Tools rm CreateDeployPackage --url "$ARA_URI" --username "$ARA_SvcAccName" --password "$ARA_SvcAccPass" --name "$($JSON.Package.Name)" -o "$ARA_SvcAccName" --folder "$($JSON.Package.Folder)" --type "$($JSON.Package.Type)" --application "$($JSON.Application.Name)"
                            $Response.ContentType = 'text/plain'
                            $ToolsOutput | % { $Message+="$($_)`r`n" } 
                            
                        }
                        
                    }
                    "application/" { $response.ContentType = 'text/plain'; $message = "Feature not yet supported" }
                    "environment/" { $response.ContentType = 'text/plain'; $message = "Feature not yet supported" }
                    "deployactivity/" { $response.ContentType = 'text/plain'; $message = "Feature not yet supported" }
                    "deploymenttarget/" { $response.ContentType = 'text/plain'; $message = "Feature not yet supported" }
                    "activityfromtemplate/" { $response.ContentType = 'text/plain'; $message = "Feature not yet supported" }
                    "profile/" { $response.ContentType = 'text/plain'; $message = "Feature not yet supported" }
                }

            }
            "execute/" { 
                Switch -Wildcard ($request.url.segments[2]) { 
                    "general/" { 
                        if ($request.HttpMethod -eq "POST") {
                            if ($request.HasEntityBody -eq $true) {
                                $StreamReader = New-Object System.IO.StreamReader $request.InputStream
                                $JSON = $StreamReader.ReadToEnd() | ConvertFrom-Json
                                <# have to retrieve JSON object --> ARA dynamic property from config per execution
                                # add switching for workflow name, also for mapping type (i.e. json->dynamic property or json->execution prompt)
                                $Config.GeneralExecution.DataMap | % { 
                                    $x = $_.dynprop
                                    $y = Invoke-Expression "`$JSON.$($_.json)"
                                }
                                #>
                                $StreamReader.Close()
                                $response.ContentType = 'text/plain'; $message = "Feature not yet supported"
                            }
                        } elseif ($request.HttpMethod -eq "GET") {  
                            # Expects something like /execute/jobp.full_jobname
                            $response.ContentType = 'text/plain'
                            if($request.Url.Segments.Count -eq 3) {
                                $request.Url.Segments
                                $jobname = $request.Url.Segments[2].Replace('/','')
                                $message = "Executing: $jobname"
                            } else {
                                $message = "Execution request malformed, please use: `n`r`n/execute/<job name>`n`r`nWhere <job name> is an executable object in the automation engine."
                            }
                        }
                    }
                    "application/" {
                        $response.ContentType = 'text/plain'; $message = "Feature not yet supported"
                    }
                    "automationengine/" { 
                        $response.ContentType = 'text/plain'; $message = "Feature not yet supported" 
                    } 
                }

            }
            "dist/" {
                $resource = $request.url.AbsolutePath
                $message = [System.IO.File]::ReadAllText($(Join-Path $AIR_SiteRoot $resource))
                Switch -Wildcard ([System.IO.Path]::GetExtension($resource)) {
                    ".js" { $response.ContentType = "application/x-javascript" }
                    ".css" { $response.ContentType = "text/css" }
                    ".png" { 
                        Write-Host "Request for image: $resource" -ForegroundColor Red -BackgroundColor Black
                        $response.ContentType = "image/png" 
                        $message = [System.IO.File]::ReadAllBytes($(Join-Path $AIR_SiteRoot $resource))
                    } 
                }
                
            }
            "about*" {
                $response.ContentType = 'text/html'
                $message = [System.IO.File]::ReadAllText($(Join-Path $AIR_SiteRoot 'index.htm'))
            }
            "terminate*" { 
                Write-Host "Termination Request Received!" -ForegroundColor DarkRed -BackgroundColor Gray
                $Message = "AIR Agent service terminated. Goodbye!"
                $Terminate = $true
            }
            "help" { 
                $Response.ContentType = 'text/html'
                $Message = [System.IO.File]::ReadAllText($(Join-Path $AIR_SiteRoot 'help.htm'))
            }
            "404" {
                $Response.ContentType = 'text/html'
                $Message = [System.IO.File]::ReadAllText($(Join-Path $AIR_SiteRoot '404.htm'))
                
            }
            default { 
                $Response.ContentType = 'text/html'
                $Message = [System.IO.File]::ReadAllText($(Join-Path $AIR_SiteRoot '404.htm'))
                $Response.Redirect("/404")
            }
        }
    } Else { 
        $Message = ""
        $Response.Redirect("/about")
    }

    If ($Message.GetType() -ne [byte[]]) {
        [byte[]] $Buffer = [System.Text.Encoding]::UTF8.GetBytes($Message)
    } Else {
        [byte[]] $Buffer = $Message
    }
    $Response.ContentLength64 = $buffer.Length
    $Output = $Response.OutputStream
    $Output.Write($buffer,0,$buffer.Length)
    $Output.Close()    

    if($Terminate) { 
        $HttpListener.Stop()
        $Terminate = $null
    }

}

 14,650 total views,  2 views today

1 Comment

  1. Vic

    Hi, Rex.

    Thanks for sharing,

    Started to look inside ForestAir.rar.
    In config.xml there is a reference to ARATools.jar.
    C:\Automic\LOCAL_INSTALL\Agents\windows\bin\ARATools.jar

    Didn’t find ARATools.jar in rar archive.
    $ARA_Tools = $Config.ARA.Tools is used in air.ps1

Leave a Reply to Vic Cancel reply

Your email address will not be published. Required fields are marked *