Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create routes "dynamically" in foreach loop #1022

Closed
SeriousMikeDE opened this issue Oct 13, 2022 · 6 comments
Closed

Create routes "dynamically" in foreach loop #1022

SeriousMikeDE opened this issue Oct 13, 2022 · 6 comments

Comments

@SeriousMikeDE
Copy link

Hello everyone,

I'm setting up Pode to have a web interface to call PowerShell scripts. The usecases vary from Jira executing AD scripts to Jenkins to copy files from a to b.

I expect the usecases to grow enormously when my colleagues understand the potential. Therefore I'd like to make it easy to add new scripts/pages/routes.

Has anyone experimented with this?

I'm thinking of dropping a script in a subfolder as well as a script containing the ScriptBlock for Add-PodeRoute

Additionally some "config file" i.e. JSON which lists all routes, the name of the page, AD groups/users which are allowed to run the script.

In server.ps1 I'd only have the general stuff like port, certificate, etc. and a foreach loop which walks through the JSON and does Add-PodeRoute for each.

Any better idea, feedback, etc? Do you think it's possible like that?

Greetings
Mike

PS: Epic project!

@RobinBeismann
Copy link
Contributor

Check #964, there I'm basically doing exactly this, just without an additional JSON.

@SeriousMikeDE
Copy link
Author

Hey @RobinBeismann

Thanks for the hint. Your setup looks much more complicated than my idea 😂

I'll give it a try. Maybe I can skip the JSON and put the parameters in the helper script directly (so first it's called with some parameter to return its requirements which are then put in the Add-PodeRoute command to call the script)

@jbaechtelMT
Copy link

jbaechtelMT commented Oct 16, 2022

Hey SeriousMikeDE - I basically had the same use case needs. When I was first learning Pode I didn't like how many times I had to go back and forth to the documentation/examples trying to decide what nested inside what other elements and what were the parameters for each element. My goals were to design our site(s) for others that come after me to easily understand and trying to help keep them from having to repeat my learning curve pains.

So I built our site "configuration" into a JSON format stored in a file. Then I wrote our Server.ps1 script to load/execute the needed "elements" from the JSON config. It is not fully finished (where does the time go?) but it does function for: Endpoints; Routes; Logging; & Sessions.

You could use the "File Change" monitoring method of Restart-PodeServer command to load your new scripts; routes; etc.

My current "demo" files loads 3 routes (Ping; Hello; Restart) from the JSON config file to give you a couple of examples to follow.

I haven't fully "adopted" the JSON format concept because of its' inability to support comments (I like a lot of comments so others know what is happening). On day I will adopt another format which does allow comments and convert over to that.

If you (or others) contact me at BaechtelMT1@gmail.com I will provide my JSON config and Server.ps1 files.

P.S. I also have scripts for Jira; Jenkins; and several other APIs that I call as Pode Routes if you are interested. Glad to share!

P.S.S. I also think Pode and Pode.Web are awesome projects! I have been converting all of my sites/APIs over to Pode from PoSHServer. I have found Pode to be a real time saver and it reduces coding time/effort.

Thanks, Joe

@jbaechtelMT
Copy link

jbaechtelMT commented Oct 16, 2022

For any given parameter of and given element in the Config.JSON, if the value of the parameter is "false" it won't be sent as an argument of the given element command to Pode. If you want to use a given parameter, just change the value to "True" (for switch type parameters) or to a string value contained inside of quotes:

config.zip
server.zip

@jacobswhit-copart
Copy link

jacobswhit-copart commented Nov 18, 2022

Just wanted to add this here as I recently did something similar for an application.

In my main server file, this logic looks within a 'routes' folder for .psd1 files and subfolders (for route groups).
The object in a given .psd1 is splatted as the parameters for a 'Add-PodeRoute' or 'Add-PodeRouteGroup' call.
As a point of convention, I've taken to naming the route data and script files the same.

Within server.ps1

        # Load routes.
        # https://badgerati.github.io/Pode/Tutorials/Routes/Overview/
        (Get-ChildItem './routes/' -Filter *.psd1)
        | ForEach-Object {
            $Splat = (Import-PowerShellDataFile $_.FullName)
            Add-PodeRoute @Splat
        }
        # Load route groups.
        (Get-ChildItem './routes/' -Directory)
        | ForEach-Object {
            $RouteGroup = $_
            $Splat = (Import-PowerShellDataFile (Join-Path $RouteGroup.Fullname 'group.psd1'))
            Add-PodeRouteGroup @Splat -Routes {
                (Get-ChildItem ($using:RouteGroup) -Filter *.psd1)
                | ForEach-Object {
                    if ($_.BaseName -ne 'group') {
                        $GroupRoute = (Import-PowerShellDataFile $_.FullName)
                        Add-PodeRoute @GroupRoute
                    }
                }
            }
        }

Example structure

-server.ps1
-routes/
    -ping.ps1
    -ping.psd1
    -<route-name>.ps1
    -<route-name>.psd1
    -<group-name>/
        -group.psd1
        -<group-route>.ps1
        -<group-route>.psd1

ping.psd1

@{
    Method = 'GET'
    Path = '/ping'
    FilePath = './routes/ping.ps1'
}

ping.ps1

{
    Write-PodeJsonResponse "Pong!"
}

I've found this to be extensible so far and I thought the use of psd1/splat felt very "powershell".

@SeriousMikeDE
Copy link
Author

To finish this here's what I setup now.

In server.ps1 I have the following:

$Pages = Get-ChildItem .\pages\ -Filter "*.ps1"

foreach($Page in $Pages) {
    # Gather information from script by calling it with -Info
    $Auth, $Method, $URI = & $Page.FullName -Info
	try {
		# Check, if scheme already exists (i.e. if previous page uses the same)
		Get-PodeAuth -Name $Auth.Name
	}
	catch {
		# Error expected
		# Auth scheme doesn't exist and will be created
		New-PodeAuthScheme -Basic | Add-PodeAuthWindowsAd -KeepCredential -Name $Auth.Name -Groups $Auth.Groups -Users $Auth.Users
	}
    # Create Route
    Add-PodeRoute -Method $Method -Path ("/"+$URI) -Authentication $Auth.Name -FilePath $Page.FullName
}

this loops through the subfolder "pages" and searches for all ps1 files

Each ps1 file has the following content:

[CmdletBinding()]
param (
    [Switch] $Info # Set to only return information
)

if($Info) {
    $Authentication = @{
        Name = "Welcome"
        Groups = @("some_AD_group")
        Users = @()
    }
    $Method = "Get"
    $URI = "welcome"
    return $Authentication, $Method, $URI
}
{
# the actual code of the routes script block
}

With this setup I only need to create a new file in the subfolder, fill in the infos and add the code block. After server restart the new sub page is available

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants