Deploy a pipeline of AWS SAM stacks
Project description
samstacks
samstacks – A YAML driven pipeline of AWS SAM stacks inspired by GitHub Actions.
Deploy a pipeline of AWS SAM stacks using a YAML manifest with GitHub Actions-style syntax.
Table of Contents
- Installation
- Quick Start
- Examples
- CLI Commands
- Manifest Reference (Detailed)
- Troubleshooting / FAQ
- Development
Installation
Install samstacks using pip:
# Recommended to install in a virtual environment
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install samstacks
Prerequisites
- Python 3.12 or higher
- AWS SAM CLI installed and configured (run
sam --versionto check). - AWS CLI configured with appropriate credentials (run
aws sts get-caller-identityto check).
Quick Start
-
Install
samstacks(see Installation above). -
Create a manifest file (e.g.,
pipeline.yml):# pipeline.yml pipeline_name: MySimpleApp stacks: - id: backend dir: my_sam_app/backend/ # Path relative to this pipeline.yml params: TableName: MyTable - id: frontend dir: my_sam_app/frontend/ params: ApiEndpoint: ${{ stacks.backend.outputs.ApiUrl }} # Example of output passing
(This is a minimal example. See Manifest Reference for all options.)
-
Deploy the pipeline:
# Ensure environment variables used in the manifest (if any) are set # export MY_ENV_VAR=some_value samstacks deploy pipeline.yml
Examples
Want a full working demo? Check out the S3 Object Processor example in the examples/ directory. It showcases:
- S3 bucket with SQS notifications
- Lambda function processing uploaded files
- Stack output dependencies
- Templating for parameters and
samconfig.toml - Conditional deployment (
if) - Post-deployment testing scripts (
run)
To try it (ensure AWS credentials and region are configured, and you are in the project root):
export ENVIRONMENT=dev
export PROJECT_NAME=samstacks-demo
samstacks deploy examples/simple-pipeline.yml
CLI Commands
Deploy a Pipeline
samstacks deploy <manifest-file> [OPTIONS]
Deploys the stacks defined in the manifest file. SAM CLI's sam deploy output is streamed in real-time.
By default, if SAM reports "No changes to deploy" for a stack, samstacks will automatically attempt to delete the resultant 'FAILED' changeset.
Options:
--region <region>: Override the default AWS region.--profile <profile>: Override the default AWS CLI profile.--auto-delete-failed: Enables proactive cleanup. Before attempting to deploy a stack, this option will:- Automatically delete the stack if it's found in
ROLLBACK_COMPLETEstate. - Automatically delete any pre-existing 'FAILED' changesets for the stack that have the reason "No updates are to be performed."
- Automatically delete the stack if it's found in
--debug: Enable debug logging.--quiet: Suppress all output except errors.
Validate a Manifest
samstacks validate <manifest-file>
Validates the manifest file, checking syntax and ensuring stack directories exist.
Manifest Reference
This tool uses a YAML manifest file (e.g., pipeline.yml) to define the pipeline of AWS SAM stacks to be deployed. The manifest allows for defining dependencies between stacks by piping outputs from one stack as parameters into another.
Top-Level Structure
pipeline_name: My SAM Application Deployment
pipeline_description: Deploys the backend and frontend for My SAM Application.
pipeline_settings:
# ... see below ...
stacks:
- # ... see below ...
- # ... see below ...
pipeline_name: (String) The overall name for your deployment pipeline.pipeline_description: (String, Optional) A brief description of the pipeline's purpose.
pipeline_settings
Global configurations that apply to all stacks in the pipeline, unless overridden at the stack level.
stack_name_prefix: (String, Optional) A string prepended to each stack'sidto form the CloudFormation stack name. Supports template substitution.stack_name_suffix: (String, Optional) A string appended after the stackidand any per-stack suffix. Supports template substitution.default_region: (String, Optional) Global AWS region for stack deployments. Can be overridden per stack or by the--regionCLI option.default_profile: (String, Optional) Global AWS CLI profile for stack deployments. Can be overridden per stack or by the--profileCLI option.
stacks
A list of SAM stack definitions to be processed sequentially. Each item in the list is an object with the following keys:
id: (String, Required) A unique identifier for the stack within the pipeline. Used for output referencing (e.g.,${{ stacks.<id>.outputs.OutputName }}) and forms the core of the CloudFormation stack name.name: (String, Optional) A human-readable name for logging and display. Does not affect the deployed CloudFormation stack name. Defaults to theidif not provided.description: (String, Optional) A description for the stack.dir: (String, Required) Path to the directory containing the stack'stemplate.yaml(ortemplate.yml),samconfig.toml(optional), and source code. This path is resolved relative to the location of the manifest file itself.stack_name_suffix: (String, Optional) A stack-specific suffix, appended after theidand before any global suffix.region: (String, Optional) Overrides global/default AWS region for this specific stack.profile: (String, Optional) Overrides global/default AWS profile for this specific stack.params: (Object, Optional) Key-value map of parameters forsam deploy --parameter-overrides. Values support template substitution.if: (String, Optional) A condition to determine if the stack should be deployed. Supports template substitution. If omitted, the stack is always processed. See "Conditional Stack Deployment" below.run: (String, Optional) A shell script (can be multi-line using|) executed after successful deployment and output retrieval for this stack. Supports template substitution. Runs in the stack'sdir. See "Post-deployment Scripts" below.
Templating in Manifest Values
Several fields in the manifest support template substitution using the ${{ <expression> }} syntax.
-
Environment Variables:
${{ env.VARIABLE_NAME }}- Substitutes the value of the environment variable
VARIABLE_NAME. - If the variable is not set, it's treated as
None(which is falsy for the||operator).
- Substitutes the value of the environment variable
-
Stack Outputs:
${{ stacks.<source_stack_id>.outputs.<OutputName> }}- Substitutes the value of
<OutputName>from the outputs of the stack identified by<source_stack_id>(which must have been deployed earlier in the pipeline). - If the stack or the specific output is not found, it's treated as
None(falsy for the||operator).
- Substitutes the value of
-
Default Value Fallback (
||): The||operator can be used within an expression to provide a fallback value if the preceding part is falsy (e.g., an unset variable, an empty string from a resolved variable, or a non-existent stack output).- Syntax:
${{ <expr1> || <expr2> || ... || 'literal_fallback' }} - It evaluates expressions from left to right and uses the first truthy (non-empty, resolved) value.
- Literals: String literals used as fallbacks must be enclosed in single or double quotes (e.g.,
'default-value',"another default"). - An empty string (
''or"") from a resolved variable or as a literal fallback is considered falsy by||, meaning the next part of the chain will be evaluated. - If all parts of a fallback chain are falsy, the expression resolves to the value of the last part in the chain. If the last part was an unresolvable variable/output (resolved to
None), the final result is an empty string. If the last part was a literal empty string (''), the result is that empty string.
- Syntax:
Applicable fields for templating: pipeline_settings.stack_name_prefix, pipeline_settings.stack_name_suffix, stacks.params values, stacks.if conditions, stacks.run script content, and stacks.stack_name_suffix.
Execution Order and Stack Naming
- Stacks are deployed sequentially in the order they appear in the
stackslist, provided theirifcondition (if present) evaluates to true. - The actual CloudFormation stack name is constructed as:
[pipeline_settings.stack_name_prefix][stack.id][stack.stack_name_suffix][pipeline_settings.stack_name_suffix]. Empty parts are omitted. (Example:id: api, global prefixdev-, stack suffix-v2, global suffix-appresults indev-api-v2-app) - This constructed name always overrides any
stack_namedefined in a stack'ssamconfig.tomlwhen deploying viasamstacks.
SAM Deployment Parameters
When deploying each stack, samstacks automatically sets several SAM CLI parameters to ensure consistent and isolated deployments:
--stack-name: Always set to the constructed stack name (as described above), overriding anystack_nameinsamconfig.toml.--s3-prefix: Automatically set to match the stack name, ensuring S3 deployment artifacts are organized by stack.--resolve-s3: Automatically enabled to let SAM create and manage the S3 bucket for deployment artifacts.
This means that even if your samconfig.toml files specify different values for these parameters, samstacks will override them to maintain consistency across the pipeline. Other samconfig.toml settings (like capabilities, region, tags, etc.) are still respected and can be templated with environment variables.
samconfig.toml Preprocessing
samstacks supports preprocessing of samconfig.toml files found in a stack's dir before they are used by sam build and sam deploy.
- Syntax:
${{ env.VARIABLE_NAME }}can be used withinsamconfig.tomlvalues. - Behavior: Placeholders are replaced with corresponding environment variable values. If an environment variable is not set, it will be replaced with an empty string.
- Scope: This templating is currently limited to
${{ env.VARIABLE_NAME }}. It does not support${{ stacks.<id>.outputs.<OutputName> }}or the||fallback operator withinsamconfig.tomlitself. - Use Case: This allows for dynamic
samconfig.tomlsettings (e.g.,s3_bucket,s3_prefix,image_repositories,tags) based on the execution environment.
# Example: stacks/api/samconfig.toml
version = 0.1
[default.deploy.parameters]
resolve_s3 = true
s3_prefix = "${{ env.PROJECT_NAME }}/api-artifacts"
capabilities = "CAPABILITY_IAM"
region = "${{ env.AWS_TARGET_REGION }}" # Assumes AWS_TARGET_REGION is set
tags = 'Project="${{ env.PROJECT_NAME || 'DefaultProject' }}" CostCenter="${{ env.COST_CENTER }}"'
Conditional Stack Deployment (if field)
- Each stack definition can optionally include an
if: "<condition_string>"field. - The
<condition_string>is processed using the templating engine (supportingenvvariables,stackoutputs, and the||fallback operator). - The stack is deployed if the final, substituted string evaluates to true (case-insensitive:
"true","1","yes","on"). - Otherwise, the stack is skipped, and its outputs will not be available for subsequent stacks.
Post-deployment Scripts (run field)
- Each stack definition can optionally include a
run: "<shell_script_content>"field. - The script content is processed using the templating engine.
- It's executed after the stack has been successfully deployed and its outputs have been retrieved.
- The script runs with the stack's
diras its current working directory. - A non-zero exit code from any command in the script will cause the
runstep to be considered failed, and the entiresamstackspipeline will halt. - Security Note: Manifest files containing
runscripts allow arbitrary shell command execution. Only use manifests from trusted sources.
Automatic Cleanup of CloudFormation Artifacts
samstacks includes features to help manage CloudFormation artifacts automatically:
-
Default Behavior (Reactive Cleanup): If an attempt to deploy a stack with
sam deployresults in SAM CLI reporting "No changes to deploy. Stack ... is up to date",samstackswill automatically try to delete the 'FAILED' changeset that SAM CLI creates in this specific situation. This helps keep the list of changesets for your stack clean. -
--auto-delete-failedFlag (Proactive Cleanup): When you use this CLI flag,samstacksperforms additional cleanup actions before attempting to deploy each stack:- Deletes
ROLLBACK_COMPLETEStacks: If a stack is found in theROLLBACK_COMPLETEstate (often indicating a failed initial creation with no resources provisioned), it will be deleted. - Deletes Old "No Update" Changesets: Any pre-existing 'FAILED' changesets associated with the stack that have the status reason "No updates are to be performed." will be deleted.
- Deletes
This combination helps maintain a cleaner CloudFormation environment, especially during iterative development.
Troubleshooting / FAQ
-
Manifest Validation Errors:
- Ensure your YAML syntax is correct.
- Check that all required fields (like
idanddirfor each stack) are present. - Verify that paths specified in
direxist relative to your manifest file.
-
Template Substitution Issues (
${{ ... }}):- Unresolved
envvariables: If${{ env.MY_VAR }}results in an empty string or an unexpected default, ensureMY_VARis correctly set in your shell environment before runningsamstacks. The||operator can provide defaults:${{ env.MY_VAR || 'default_value' }}. - Unresolved
stacksoutputs: If${{ stacks.some_stack.outputs.SomeOutput }}is not working:- Confirm
some_stackis defined before the current stack in the manifest. - Check that
some_stackdeployed successfully and actually producesSomeOutput(case-sensitive). - Ensure
some_stackwas not skipped due to anifcondition.
- Confirm
- Literals in fallbacks: Remember to quote string literals used with the
||operator:${{ env.VAR || 'this is a string' }}.
- Unresolved
-
if:Condition Not Behaving as Expected:- The
ifcondition evaluates the final string value after templating. For truthiness, it checks against"true","1","yes","on"(case-insensitive). - If you are checking an environment variable, ensure it's set to one of these values. For example,
if: ${{ env.SHOULD_DEPLOY || 'false' }}means it deploys ifSHOULD_DEPLOYis a truthy string, or defaults to not deploying ifSHOULD_DEPLOYis unset/empty.
- The
-
Stack in
ROLLBACK_COMPLETE:- This usually means the initial stack creation failed before any resources were provisioned. CloudFormation cannot update a stack in this state; it can only be deleted.
- Use the
--auto-delete-failedflag withsamstacks deployto automatically delete such stacks before retrying deployment.
-
"No updates are to be performed." FAILED Changesets Accumulating:
samstacksautomatically deletes the changeset created by SAM when it reports "No changes to deploy."- For older, similar FAILED changesets, use the
--auto-delete-failedflag during deployment to clean them up proactively.
-
Path Resolution for
dir:- The
dirspecified for each stack in the manifest is always resolved relative to the location of the manifest file itself, not relative to where you run thesamstackscommand.
- The
-
AWS Credentials or Region Issues:
- Ensure your AWS CLI is configured correctly (
aws configureor environment variables likeAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_DEFAULT_REGION). - You can specify region and profile via
pipeline_settingsin the manifest or via CLI options (--region,--profile).
- Ensure your AWS CLI is configured correctly (
Development
Setup Development Environment
- Clone the repository:
git clone https://github.com/alessandro-bologna/samstacks.git
cd samstacks
- Create a virtual environment using
uvand install dependencies:
uv venv .venv
# Or python -m venv .venv if you don't have uv integrated for venv creation yet
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -e ".[dev]" # Installs in editable mode with dev dependencies
- Run tests:
pytest
- Run linting and formatting using
ruff:
ruff format samstacks tests # Format code
ruff check --fix --isolated samstacks tests # Lint and automatically fix issues, using only project config
# Optionally, run mypy for deeper static type checking if it's still part of your workflow:
# mypy samstacks
Project Structure
samstacks/
├── samstacks/ # Main package
│ ├── __init__.py
│ ├── cli.py # Command-line interface (Click)
│ ├── core.py # Core Pipeline and Stack classes
│ ├── templating.py # Template processing engine
│ ├── aws_utils.py # AWS SDK (boto3) utilities
│ └── exceptions.py # Custom exception classes
├── tests/ # Pytest test suite
├── examples/ # Example manifest files and SAM stacks
│ ├── simple-pipeline.yml
│ └── stacks/
│ ├── processor/
│ └── storage/
├── .github/workflows/ # GitHub Actions CI workflows (if any)
├── pyproject.toml # Project metadata, dependencies (Poetry/Hatch)
├── README.md # This file
└── ... # Other config files (.gitignore, .editorconfig, etc.)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file samstacks-0.1.3.tar.gz.
File metadata
- Download URL: samstacks-0.1.3.tar.gz
- Upload date:
- Size: 33.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ec62534b8f553aa7201a98266842a4e9fce2d620f0215612f444e89f4eed0ea
|
|
| MD5 |
a97e2beddcebc993ee88bdb97f5bd75f
|
|
| BLAKE2b-256 |
e7584d1c6a22461869bab14ef12c77d183ec8d6c7205c943a4b6532065728bd3
|
File details
Details for the file samstacks-0.1.3-py3-none-any.whl.
File metadata
- Download URL: samstacks-0.1.3-py3-none-any.whl
- Upload date:
- Size: 33.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44b0006f06151feb53c6c9bbf81319dbf0a861702a35a1f9ef6647e71ee96531
|
|
| MD5 |
30d3077bd221ce43884afdeaba4b36b8
|
|
| BLAKE2b-256 |
27fcfc65f04bb349325a6e493f00985bf6dc3310507ee36d23d09ac4ce2360f1
|