forked from aws/aws-sam-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* two-stages-pipeline plugin * typos * add docstring * make mypy happy * removing swap file * delete the two_stages_pipeline plugin as the pipeline-bootstrap command took over its responsibility * remove 'get_template_function_runtimes' function as the decision is made to not process the SAM template during pipeline init which was the only place we use the function * sam pipeline bootstrap command * move the pipelineconfig.toml file to .aws-sam * UX - rewriting Co-authored-by: Chris Rehn <[email protected]> * UX improvements * make black happy * apply review comments * UX - rewriting Co-authored-by: Chris Rehn <[email protected]> * refactor * Apply review comments * use python way of array elements assignments * Update samcli/lib/pipeline/bootstrap/stage.py Co-authored-by: _sam <[email protected]> * apply review comments * typo * read using utf-8 * create and user a safe version of the save_config method * apply review comments * rename _get_command_name to _get_command_names * don't save generated ARNs for now, will save during init * Revert "don't save generated ARNs for now, will save during init" This reverts commit d184e16. * Notify the user to rotate periodically rotate the IAM credentials * typo * Use AES instead of KMS for S3 SSE * rename Ecr to ECR and Iam to IAM * Grant lambda service explicit permissions to thhe ECR instead of relying on giving this permissions on ad-hoc while creating the container images Co-authored-by: Chris Rehn <[email protected]> Co-authored-by: _sam <[email protected]>
- Loading branch information
1 parent
321caf5
commit 89ef6d4
Showing
34 changed files
with
2,069 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
""" | ||
CLI command for "pipeline bootstrap", which sets up the require pipeline infrastructure resources | ||
""" | ||
import os | ||
from typing import Any, Dict, List, Optional | ||
|
||
import click | ||
|
||
from samcli.cli.cli_config_file import configuration_option, TomlProvider | ||
from samcli.cli.context import get_cmd_names | ||
from samcli.cli.main import pass_context, common_options, aws_creds_options, print_cmdline_args | ||
from samcli.lib.config.samconfig import SamConfig | ||
from samcli.lib.pipeline.bootstrap.stage import Stage | ||
from samcli.lib.telemetry.metric import track_command | ||
from samcli.lib.utils.version_checker import check_newer_version | ||
from .guided_context import GuidedContext | ||
|
||
SHORT_HELP = "Sets up infrastructure resources for AWS SAM CI/CD pipelines." | ||
|
||
HELP_TEXT = """Sets up the following infrastructure resources for AWS SAM CI/CD pipelines: | ||
\n\t - Pipeline IAM user with access key ID and secret access key credentials to be shared with the CI/CD provider | ||
\n\t - Pipeline execution IAM role assumed by the pipeline user to obtain access to the AWS account | ||
\n\t - CloudFormation execution IAM role assumed by CloudFormation to deploy the AWS SAM application | ||
\n\t - Artifacts S3 bucket to hold the AWS SAM build artifacts | ||
\n\t - Optionally, an ECR repository to hold container image Lambda deployment packages | ||
""" | ||
|
||
PIPELINE_CONFIG_DIR = os.path.join(".aws-sam", "pipeline") | ||
PIPELINE_CONFIG_FILENAME = "pipelineconfig.toml" | ||
|
||
|
||
@click.command("bootstrap", short_help=SHORT_HELP, help=HELP_TEXT, context_settings=dict(max_content_width=120)) | ||
@configuration_option(provider=TomlProvider(section="parameters")) | ||
@click.option( | ||
"--interactive/--no-interactive", | ||
is_flag=True, | ||
default=True, | ||
help="Disable interactive prompting for bootstrap parameters, and fail if any required arguments are missing.", | ||
) | ||
@click.option( | ||
"--stage-name", | ||
help="The name of the corresponding pipeline stage. It is used as a suffix for the created resources.", | ||
required=False, | ||
) | ||
@click.option( | ||
"--pipeline-user", | ||
help="The ARN of the IAM user having its access key ID and secret access key shared with the CI/CD provider. " | ||
"It is used to grant this IAM user the permissions to access the corresponding AWS account. " | ||
"If not provided, the command will create one along with access key ID and secret access key credentials.", | ||
required=False, | ||
) | ||
@click.option( | ||
"--pipeline-execution-role", | ||
help="The ARN of an IAM role to be assumed by the pipeline user to operate on this stage. " | ||
"Provide it only if you want to user your own role, otherwise, the command will create one", | ||
required=False, | ||
) | ||
@click.option( | ||
"--cloudformation-execution-role", | ||
help="The ARN of an IAM role to be assumed by the CloudFormation service while deploying the application's stack. " | ||
"Provide it only if you want to user your own role, otherwise, the command will create one.", | ||
required=False, | ||
) | ||
@click.option( | ||
"--artifacts-bucket", | ||
help="The ARN of an S3 bucket to hold the AWS SAM build artifacts. " | ||
"Provide it only if you want to user your own S3 bucket, otherwise, the command will create one.", | ||
required=False, | ||
) | ||
@click.option( | ||
"--create-ecr-repo/--no-create-ecr-repo", | ||
is_flag=True, | ||
default=False, | ||
help="If set to true and no ECR repository is provided, this command will create an ECR repository to hold the" | ||
" container images of Lambda functions having an Image package type.", | ||
) | ||
@click.option( | ||
"--ecr-repo", | ||
help="The ARN of an ECR repository to hold the containers images of Lambda functions of Image package type. " | ||
"If provided, the --create-ecr-repo argument is ignored. If not provided and --create-ecr-repo is set to true, " | ||
"the command will create one.", | ||
required=False, | ||
) | ||
@click.option( | ||
"--pipeline-ip-range", | ||
help="If provided, all requests coming from outside of the given range are denied. Example: 10.24.34.0/24", | ||
required=False, | ||
) | ||
@click.option( | ||
"--confirm-changeset/--no-confirm-changeset", | ||
default=True, | ||
is_flag=True, | ||
help="Prompt to confirm if the resources is to be deployed by SAM CLI.", | ||
) | ||
@common_options | ||
@aws_creds_options | ||
@pass_context | ||
@track_command | ||
@check_newer_version | ||
@print_cmdline_args | ||
def cli( | ||
ctx: Any, | ||
interactive: bool, | ||
stage_name: Optional[str], | ||
pipeline_user: Optional[str], | ||
pipeline_execution_role: Optional[str], | ||
cloudformation_execution_role: Optional[str], | ||
artifacts_bucket: Optional[str], | ||
create_ecr_repo: bool, | ||
ecr_repo: Optional[str], | ||
pipeline_ip_range: Optional[str], | ||
confirm_changeset: bool, | ||
config_file: Optional[str], | ||
config_env: Optional[str], | ||
) -> None: | ||
""" | ||
`sam pipeline bootstrap` command entry point | ||
""" | ||
do_cli( | ||
region=ctx.region, | ||
profile=ctx.profile, | ||
interactive=interactive, | ||
stage_name=stage_name, | ||
pipeline_user_arn=pipeline_user, | ||
pipeline_execution_role_arn=pipeline_execution_role, | ||
cloudformation_execution_role_arn=cloudformation_execution_role, | ||
artifacts_bucket_arn=artifacts_bucket, | ||
create_ecr_repo=create_ecr_repo, | ||
ecr_repo_arn=ecr_repo, | ||
pipeline_ip_range=pipeline_ip_range, | ||
confirm_changeset=confirm_changeset, | ||
config_file=config_env, | ||
config_env=config_file, | ||
) # pragma: no cover | ||
|
||
|
||
def do_cli( | ||
region: Optional[str], | ||
profile: Optional[str], | ||
interactive: bool, | ||
stage_name: Optional[str], | ||
pipeline_user_arn: Optional[str], | ||
pipeline_execution_role_arn: Optional[str], | ||
cloudformation_execution_role_arn: Optional[str], | ||
artifacts_bucket_arn: Optional[str], | ||
create_ecr_repo: bool, | ||
ecr_repo_arn: Optional[str], | ||
pipeline_ip_range: Optional[str], | ||
confirm_changeset: bool, | ||
config_file: Optional[str], | ||
config_env: Optional[str], | ||
) -> None: | ||
""" | ||
implementation of `sam pipeline bootstrap` command | ||
""" | ||
if not pipeline_user_arn: | ||
pipeline_user_arn = _load_saved_pipeline_user_arn() | ||
|
||
if interactive: | ||
guided_context = GuidedContext( | ||
stage_name=stage_name, | ||
pipeline_user_arn=pipeline_user_arn, | ||
pipeline_execution_role_arn=pipeline_execution_role_arn, | ||
cloudformation_execution_role_arn=cloudformation_execution_role_arn, | ||
artifacts_bucket_arn=artifacts_bucket_arn, | ||
create_ecr_repo=create_ecr_repo, | ||
ecr_repo_arn=ecr_repo_arn, | ||
pipeline_ip_range=pipeline_ip_range, | ||
) | ||
guided_context.run() | ||
stage_name = guided_context.stage_name | ||
pipeline_user_arn = guided_context.pipeline_user_arn | ||
pipeline_execution_role_arn = guided_context.pipeline_execution_role_arn | ||
pipeline_ip_range = guided_context.pipeline_ip_range | ||
cloudformation_execution_role_arn = guided_context.cloudformation_execution_role_arn | ||
artifacts_bucket_arn = guided_context.artifacts_bucket_arn | ||
create_ecr_repo = guided_context.create_ecr_repo | ||
ecr_repo_arn = guided_context.ecr_repo_arn | ||
|
||
if not stage_name: | ||
raise click.UsageError("Missing required parameter '--stage-name'") | ||
|
||
stage: Stage = Stage( | ||
name=stage_name, | ||
aws_profile=profile, | ||
aws_region=region, | ||
pipeline_user_arn=pipeline_user_arn, | ||
pipeline_execution_role_arn=pipeline_execution_role_arn, | ||
pipeline_ip_range=pipeline_ip_range, | ||
cloudformation_execution_role_arn=cloudformation_execution_role_arn, | ||
artifacts_bucket_arn=artifacts_bucket_arn, | ||
create_ecr_repo=create_ecr_repo, | ||
ecr_repo_arn=ecr_repo_arn, | ||
) | ||
|
||
bootstrapped: bool = stage.bootstrap(confirm_changeset=confirm_changeset) | ||
|
||
if bootstrapped: | ||
stage.print_resources_summary() | ||
|
||
stage.save_config_safe( | ||
config_dir=PIPELINE_CONFIG_DIR, filename=PIPELINE_CONFIG_FILENAME, cmd_names=_get_command_names() | ||
) | ||
|
||
|
||
def _load_saved_pipeline_user_arn() -> Optional[str]: | ||
samconfig: SamConfig = SamConfig(config_dir=PIPELINE_CONFIG_DIR, filename=PIPELINE_CONFIG_FILENAME) | ||
if not samconfig.exists(): | ||
return None | ||
config: Dict[str, str] = samconfig.get_all(cmd_names=_get_command_names(), section="parameters") | ||
return config.get("pipeline_user") | ||
|
||
|
||
def _get_command_names() -> List[str]: | ||
ctx = click.get_current_context() | ||
return get_cmd_names(ctx.info_name, ctx) # ["pipeline", "bootstrap"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
""" | ||
An interactive flow that prompt the user for required information to bootstrap the AWS account of a pipeline stage | ||
with the required infrastructure | ||
""" | ||
from typing import Optional | ||
|
||
import click | ||
|
||
|
||
class GuidedContext: | ||
def __init__( | ||
self, | ||
stage_name: Optional[str] = None, | ||
pipeline_user_arn: Optional[str] = None, | ||
pipeline_execution_role_arn: Optional[str] = None, | ||
cloudformation_execution_role_arn: Optional[str] = None, | ||
artifacts_bucket_arn: Optional[str] = None, | ||
create_ecr_repo: bool = False, | ||
ecr_repo_arn: Optional[str] = None, | ||
pipeline_ip_range: Optional[str] = None, | ||
) -> None: | ||
self.stage_name = stage_name | ||
self.pipeline_user_arn = pipeline_user_arn | ||
self.pipeline_execution_role_arn = pipeline_execution_role_arn | ||
self.cloudformation_execution_role_arn = cloudformation_execution_role_arn | ||
self.artifacts_bucket_arn = artifacts_bucket_arn | ||
self.create_ecr_repo = create_ecr_repo | ||
self.ecr_repo_arn = ecr_repo_arn | ||
self.pipeline_ip_range = pipeline_ip_range | ||
|
||
def run(self) -> None: | ||
""" | ||
Runs an interactive questionnaire to prompt the user for the ARNs of the AWS resources(infrastructure) required | ||
for the pipeline to work. Users can provide all, none or some resources' ARNs and leave the remaining empty | ||
and it will be created by the bootstrap command | ||
""" | ||
if not self.stage_name: | ||
self.stage_name = click.prompt("Stage Name", type=click.STRING) | ||
|
||
if not self.pipeline_user_arn: | ||
click.echo( | ||
"\nThere must be exactly one pipeline user across all of the pipeline stages. " | ||
"If you have ran this command before to bootstrap a previous pipeline stage, please " | ||
"provide the ARN of the created pipeline user, otherwise, we will create a new user for you. " | ||
"Please make sure to store the credentials safely with the CI/CD provider." | ||
) | ||
self.pipeline_user_arn = click.prompt( | ||
"Pipeline user [leave blank to create one]", default="", type=click.STRING | ||
) | ||
|
||
if not self.pipeline_execution_role_arn: | ||
self.pipeline_execution_role_arn = click.prompt( | ||
"\nPipeline execution role (an IAM role assumed by the pipeline user to operate on this stage) " | ||
"[leave blank to create one]", | ||
default="", | ||
type=click.STRING, | ||
) | ||
|
||
if not self.cloudformation_execution_role_arn: | ||
self.cloudformation_execution_role_arn = click.prompt( | ||
"\nCloudFormation execution role (an IAM role assumed by CloudFormation to deploy " | ||
"the application's stack) [leave blank to create one]", | ||
default="", | ||
type=click.STRING, | ||
) | ||
|
||
if not self.artifacts_bucket_arn: | ||
self.artifacts_bucket_arn = click.prompt( | ||
"\nArtifacts bucket (S3 bucket to hold the AWS SAM build artifacts) [leave blank to create one]", | ||
default="", | ||
type=click.STRING, | ||
) | ||
if not self.ecr_repo_arn: | ||
click.echo( | ||
"\nIf your SAM template includes (or going to include) Lambda functions of Image package type, " | ||
"then an ECR repository is required. Should we create one?" | ||
) | ||
click.echo("\t1 - No, My SAM template won't include Lambda functions of Image package type") | ||
click.echo("\t2 - Yes, I need help creating one") | ||
click.echo("\t3 - I already have an ECR repository") | ||
choice = click.prompt(text="Choice", show_choices=False, type=click.Choice(["1", "2", "3"])) | ||
if choice == "1": | ||
self.create_ecr_repo = False | ||
elif choice == "2": | ||
self.create_ecr_repo = True | ||
else: # choice == "3" | ||
self.create_ecr_repo = False | ||
self.ecr_repo_arn = click.prompt("ECR repo", type=click.STRING) | ||
|
||
if not self.pipeline_ip_range: | ||
click.echo("\nWe can deny requests not coming from a recognized IP address range.") | ||
self.pipeline_ip_range = click.prompt( | ||
"Pipeline IP address range (using CIDR notation) [leave blank if you don't know]", | ||
default="", | ||
type=click.STRING, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
""" | ||
Command group for "pipeline" suite for commands. It provides common CLI arguments, template parsing capabilities, | ||
setting up stdin/stdout etc | ||
""" | ||
|
||
import click | ||
|
||
from .bootstrap.cli import cli as bootstrap_cli | ||
|
||
|
||
@click.group() | ||
def cli() -> None: | ||
""" | ||
Manage the continuous delivery of the application | ||
""" | ||
|
||
|
||
# Add individual commands under this group | ||
cli.add_command(bootstrap_cli) |
Oops, something went wrong.