Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

powershell-lambda

Run PowerShell scripts in AWS lambda using a container image

AWS does not provide a container image for PowerShell lambdas, 823. This project provides a starting point to run PowerShell scripts from a container image.

Why

  • Lambda's deployed via zip have a limitation of 50MB compressed and 250MB expanded. Container images can be as large as 10GB!
  • Some powershell modules have an install process that relies on having write access to powershell shared objects, PSWSMan for example. Once you deploy your script to AWS, you only have write access /tmp. Such a module could be installed in a container image prior to execution.
  • Generic linux utilities and other arbitrary files are simpler to include into a container image as part of a build process compared to using lambda layers or bundling into a required module.

How it works

  • The image is based on mcr.microsoft.com/powershell, python3 is installed and a small python program implements the runtime interface.
  • The python program encodes the input event and context properties and executes a powershell init script.
  • The init.ps1 script decodes the input and context and sets variables that would normally be available in a zip deployment, i.e. $LambdaInput and $LambdaContext. This init script is also a nice place to include some generic functions or dot source other scripts not packages as modules. As an example, one function is included in init.ps1 that can write an AWS Secrets Manager binary secret value to a file, Get-SECSecretBinary.
  • init.ps1 calls your script and does whatever you want.
  • Finally the python program then collects stdout and stderr and returns an object.

Some differences to consider

The object returned

A zipped powershell lambda will return the last object output. If you wrote a script with @(1,2,3), you would get 3. This program will return all the stdout and stderr by default. Lambda responses are limited to 6MB. If you have a script with a lot of output, this could be a problem.

There are 5 different modes of output to choose from by setting the environment variable OUTPUT.

  • Default, object with stdout and stderr {"stdout": "", "stderr": ""}
  • NONE, empty string
  • STDOUT, just standard out as a string
  • STDERR, just standard error as a string
  • LAST_LINE_JSON, your object built from the last line of stdout

LAST_LINE_JSON is very useful in getting data back from powershell as an arbitrary object. All you have to do is ensure the last line contains a string that can be parsed as JSON, i.e. @(1,2,3) | ConvertTo-Json -Compress results in [1,2,3].

Return codes

Script return codes are not evaluated to determine if your script succeeded or failed.

Throwing errors

By default no errors will be thrown if your script fails, you will simply see the stdout and stderr. You can change this behavior by setting the environment variable FAIL_IF_STDERR to 1. Any output in stderr will result in an exception being raised with the content of stderr. You can get output into stderr by calling throw or Write-Error in your PowerShell script.

Missing resources

The list of resources below are not available in $LambdaContext as would be when using a zipped PowerShell lambda:

  • RemainingTime
  • Identity
  • ClientContext
  • Logger

Speed

or lack thereof

Running scripts this way is slow. You can speed things up increasing memory but don't expect sub-second response times. Even a basic script with 4GB of memory allocated will take a couple seconds to return and consume ~150MB. The motivation behind this project is largely to handle cron triggered tasks that start and complete within 15 minutes. AWS limits lambda's maximum execution time to 15 minutes.

Environment variables

NameRequiredAcceptable valuesValue if not specifiedDescription
PWSH_SCRIPTNoany string/script/example_script.ps1Path to script that will be executed, see example_script.ps1
FAIL_IF_STDERRNo1, 00Cause the lambda function to fail if there is any output in STDERR
OUTPUTNoDefault, NONE, STDOUT, STDERR, LAST_LINE_JSONDefault (both stdout and stderr)Specify if you want no output, just stdout, just stderr, or to parse the last line of your script output as a JSON object

How-to

Prerequisites

  • Ability to build linux container images: docker, podman, rancher desktop, etc.
  • AWS CLI
  • Access to lambda and ECR
  • A lambda execution role (or the ability to create one), AWS docs

Download, clone, or fork this repo.

git clone https://github.com/nickadam/powershell-lambda.git
cd powershell-lambda

Add your script and whatever.

Modify the Dockerfile or init.ps1 to suite your needs, Dockerfile reference:

  • Install linux utilities using apt-get
  • Install powershell modules using Install-Module -Name MyModule -Scope AllUsers -Force
    • -Scope AllUsers is very important since you lambda will execute as any user
  • Install additional AWS modules using Install-AWSToolsModule, S3 and SecretsManager are installed by default
  • Set desired default environment variables
  • COPY your script, or just replace example_script.ps1 in the Dockerfile
  • Add functions or whatever to init.ps1

Build, tag, and push your image, AWS docs.

docker build -t myimage:latest .
docker tag myimage:latest <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest
docker push <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Create lambda function

aws lambda create-function --region <region> --function-name MyFunction --package-type Image --code ImageUri=<accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest --role <execution role arn>

Run the function

aws lambda invoke --function-name MyFunction --payload '{ "key": "value" }' response.json

If you make changes - build, tag, push AND update the function. Updating the container image latest tag does not update the function.

aws lambda update-function-code --region <region> --function-name MyFunction <accountid>.dkr.ecr.<region>.amazonaws.com/myimage:latest

Test locally

AWS Docs

Download RIE

mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie

Run the container

docker run --rm \
-e AWS_LAMBDA_RUNTIME_API=/aws-lambda/aws-lambda-rie \
-v ~/.aws-lambda-rie:/aws-lambda \
-p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie \
python3 -m awslambdaric execpwsh.handler

Make a request

curl -s -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{ "key": "value" }'

About

Run PowerShell scripts in AWS lambda using a container image

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages