From b75a318334b8ea7c4f65bcbd2d9b500a169528c3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 20 Feb 2026 14:53:51 -0500 Subject: [PATCH] Rename AWS resources to reflect EKS Terragrunt focus; update Service Catalog product to v2.0 - Rename name_prefix: service-catalog-repo-gen -> eks-terragrunt-repo-gen - Rename CodeBuild project: service-catalog-repo-generator-builder -> eks-terragrunt-repo-generator-builder - Rename ECR repo path: service-catalog-repo-generator/lambda -> eks-terragrunt-repo-generator/lambda - Rename S3 prefix: packer-builds/service-catalog-repo-generator -> packer-builds/eks-terragrunt-repo-generator - Rename Service Catalog portfolio/product: eks-terragrunt-* - Bump product_version 1.0 -> 2.0 (adds all EKS parameters) - Update product-template.yaml LambdaFunctionArn default to new name - Update all documentation, scripts, and config files to new naming - Update .gitignore for new builder zip name --- .gitignore | 1 + CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md | 499 ++++++------------ CLOUDFORMATION_CUSTOM_RESOURCE_MIGRATION.md | 26 +- DEPLOYMENT.md | 319 ++++++----- Dockerfile | 11 +- PACKER_UPDATES.md | 161 +++--- README.md | 481 ++++++++++------- cloudformation-template.yaml | 2 +- config_packer.hcl | 13 +- deploy/.terraform_commits | 6 + deploy/main.tf | 45 +- deploy/service_catalog.tf | 19 +- deploy/terraform.tfstate | 30 +- deploy/terraform.tfstate.backup | 40 +- deploy/terraform.tfvars | 28 +- deploy/variables.tf | 10 +- design-docs/README.md | 243 ++++----- docs/SERVICE_CATALOG_RESOLUTION.md | 27 +- events/cloudformation-create-event.json | 27 +- lambda-template-repo-generator.code-workspace | 6 + main.tf | 4 +- packer.pkr.hcl | 25 +- scripts/check_github_permissions.py | 6 +- scripts/test_workflow.py | 73 ++- service-catalog/product-template.yaml | 4 +- .../__pycache__/app.cpython-311.pyc | Bin 46818 -> 52473 bytes .../__pycache__/eks_config.cpython-311.pyc | Bin 0 -> 21867 bytes .../github_provider.cpython-311.pyc | Bin 33363 -> 36396 bytes .../gitlab_provider.cpython-311.pyc | Bin 16139 -> 16191 bytes .../repository_provider.cpython-311.pyc | Bin 6415 -> 8221 bytes varfiles/default.tfvars | 10 +- variables.tf | 2 +- 32 files changed, 1100 insertions(+), 1018 deletions(-) create mode 100644 template_automation/__pycache__/eks_config.cpython-311.pyc diff --git a/.gitignore b/.gitignore index 88c64d1c..cf719090 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Packer pipeline zip files template-automation-lambda-builder.zip # Packer pipeline zip files +eks-terragrunt-repo-generator-builder.zip service-catalog-repo-generator-builder.zip diff --git a/CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md b/CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md index 709e416b..c0895c96 100644 --- a/CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md +++ b/CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md @@ -1,14 +1,15 @@ # CloudFormation Custom Resource Integration Guide -This guide explains how to use the Lambda function as a CloudFormation Custom Resource for Service Catalog. +This guide explains how the Lambda function works as a CloudFormation Custom Resource, how to set up the Service Catalog product, and how to troubleshoot issues. ## Overview -The Lambda function is invoked as a **CloudFormation Custom Resource** when someone provisions a Service Catalog product. This approach is: -- ✅ **Native to CloudFormation** - No EventBridge rules needed -- ✅ **Synchronous** - CloudFormation waits for repository creation -- ✅ **Output Support** - Repository URLs available as stack outputs -- ✅ **Error Handling** - Failures properly propagate to CloudFormation +The Lambda function is invoked as a **CloudFormation Custom Resource** when someone provisions a Service Catalog product. CloudFormation sends the parameters to the Lambda, which creates a GitHub repository, renders configuration files, and returns URLs as stack outputs. + +- ✅ **Synchronous** — CloudFormation waits for repository creation to complete +- ✅ **Output Support** — repository URL, PR URL available as stack outputs +- ✅ **Error Handling** — failures propagate back to CloudFormation +- ✅ **EKS-Aware** — auto-detects EKS parameters and renders full Terragrunt hierarchy ## How It Works @@ -17,431 +18,233 @@ User provisions Service Catalog Product ↓ CloudFormation creates stack ↓ -Custom Resource invokes Lambda +Custom::GitHubRepository resource invokes Lambda + ↓ +Lambda detects deployment type: + ├─ EKS deployment? → render 8 Terragrunt HCL files atomically + └─ Generic? → write single config.json ↓ -Lambda creates GitHub repository +Lambda creates repo, clones template, writes files, opens PR ↓ -Lambda returns success to CloudFormation +Lambda sends SUCCESS response to CloudFormation (via pre-signed URL) ↓ -Stack completes with repository URLs in outputs +Stack completes with repository URLs in Outputs tab ``` -## Step 1: Deploy the Lambda Function +## CloudFormation Templates -First, deploy the Lambda function using the Terraform configuration in the `deploy/` directory: +### EKS Deployment Template (Production) -```bash -cd deploy -terraform init -terraform apply -``` +The primary product template is `service-catalog/product-template.yaml`. It includes: +- 14 parameters organized into groups (Cluster, Account, VPC, Contact, FinOps) +- `Conditions` block to default `ClusterName` to `ProjectName` if not supplied +- `Custom::GitHubRepository` resource with **snake_case** property names +- Outputs: `RepositoryUrl`, `PullRequestUrl`, `ConfigBranch`, `ClusterName` -**Important**: Copy the `lambda_function_arn` output - you'll need this for your CloudFormation template! +**Important**: Property names use snake_case directly (not PascalCase) to avoid ambiguity with the Lambda's acronym handling in the PascalCase→snake_case normalizer (e.g., `AWSAccountId` would become `a_w_s_account_id`). -Example output: -``` -lambda_function_arn = "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation" +```yaml +Resources: + RepositoryCreator: + Type: Custom::GitHubRepository + Properties: + ServiceToken: !Ref LambdaFunctionArn + project_name: !Ref ProjectName + owning_team: !Ref OwningTeam + cluster_name: !If [ClusterNameProvided, !Ref ClusterName, !Ref ProjectName] + environment: !Ref Environment + aws_region: !Ref AwsRegion + account_name: !Ref AccountName + aws_account_id: !Ref AWSAccountId + environment_abbr: !Ref EnvironmentAbbr + vpc_name: !Ref VpcName + vpc_domain_name: !Ref VpcDomainName + cluster_mailing_list: !Ref ClusterMailingList + organization_path: !Ref OrganizationPath + finops_project_name: !Ref FinOpsProjectName + finops_project_number: !Ref FinOpsProjectNumber + +Outputs: + RepositoryUrl: + Value: !GetAtt RepositoryCreator.repository_url + PullRequestUrl: + Value: !GetAtt RepositoryCreator.pull_request_url ``` -## Step 2: Create CloudFormation Template +### Generic Template (Non-EKS) -Create a CloudFormation template that uses the Lambda as a Custom Resource. See `cloudformation-template.yaml` for a complete example: +For non-EKS use cases, see `cloudformation-template.yaml`: ```yaml -AWSTemplateFormatVersion: '2010-09-09' -Description: 'Create GitHub Repository from Template' - -Parameters: - ProjectName: - Type: String - Description: Name of the GitHub repository to create - - OwningTeam: - Type: String - Description: GitHub team that should have admin access - Default: tf-module-admins - - Environment: - Type: String - Description: Environment for the project - AllowedValues: - - development - - staging - - production - Resources: - # Custom Resource that invokes the Lambda GitHubRepository: Type: Custom::RepositoryCreator Properties: - ServiceToken: arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation + ServiceToken: !Ref LambdaFunctionArn ProjectName: !Ref ProjectName OwningTeam: !Ref OwningTeam Environment: !Ref Environment - -Outputs: - RepositoryUrl: - Description: URL of the created GitHub repository - Value: !GetAtt GitHubRepository.RepositoryUrl - - PullRequestUrl: - Description: URL of the configuration pull request - Value: !GetAtt GitHubRepository.PullRequestUrl - - RepositoryName: - Description: Name of the created repository - Value: !GetAtt GitHubRepository.RepositoryName + AwsRegion: !Ref AwsRegion ``` -### Key Points - -1. **ServiceToken**: Must be the ARN of your Lambda function -2. **Parameters**: All parameters (except `ServiceToken`) are passed to the Lambda -3. **Outputs**: Use `!GetAtt ResourceName.AttributeName` to get values from Lambda response -4. **Parameter Names**: Use PascalCase - Lambda automatically converts to snake_case - -### Available Output Attributes - -The Lambda returns these attributes that you can reference in CloudFormation outputs: - -- `RepositoryUrl` - URL of the created repository -- `RepositoryName` - Name of the repository -- `PullRequestUrl` - URL of the configuration pull request (GitHub) -- `MergeRequestUrl` - URL of the merge request (GitLab) - -## Step 3: Create Service Catalog Product - -### Option A: Using AWS Console - -1. Go to **AWS Service Catalog** → **Products** -2. Click **Create product** -3. Fill in product details: - - **Product name**: "Create GitHub Repository" - - **Description**: "Creates a new GitHub repository from template" - - **Owner**: Your team name - - **Distributor**: Your organization -4. Upload your CloudFormation template -5. Click **Create product** -6. Add the product to a portfolio -7. Grant access to users/groups - -### Option B: Using Terraform - -```hcl -resource "aws_servicecatalog_product" "github_repository" { - name = "GitHub Repository Creator" - owner = "Platform Team" - type = "CLOUD_FORMATION_TEMPLATE" - - provisioning_artifact_parameters { - name = "v1.0.0" - description = "Initial version" - type = "CLOUD_FORMATION_TEMPLATE" - template_url = "s3://my-bucket/cloudformation-template.yaml" - } - - tags = { - ManagedBy = "Terraform" - } -} - -resource "aws_servicecatalog_portfolio" "main" { - name = "Developer Self-Service" - description = "Self-service portal for developer resources" - provider_name = "Platform Team" -} - -resource "aws_servicecatalog_product_portfolio_association" "github_repo" { - portfolio_id = aws_servicecatalog_portfolio.main.id - product_id = aws_servicecatalog_product.github_repository.id -} -``` - -## Step 4: Test the Integration - -### Test 1: Direct Lambda Invocation +This writes a single `config.json` with all parameters. -Test the Lambda directly with a CloudFormation Custom Resource event: +## EKS vs Generic Detection -```bash -aws lambda invoke \ - --function-name service-catalog-repo-gen-template-automation \ - --payload file://events/cloudformation-create-event.json \ - --region us-gov-west-1 \ - response.json +The Lambda automatically routes to the EKS rendering path when **all** of these parameters are present: -cat response.json -``` +| Required for EKS | Example | +|---|---| +| `cluster_name` | `my-eks-cluster` | +| `account_name` | `csvd-dev-ew` | +| `aws_account_id` | `229685449397` | +| `vpc_name` | `csvd-dev-vpc` | +| `vpc_domain_name` | `dev.inf.csp1.census.gov` | -### Test 2: CloudFormation Stack +When detected, the Lambda: +1. Builds an `EKSDeploymentConfig` Pydantic model from the parameters +2. Renders 8 Jinja2 templates from `template_automation/templates/eks/` +3. Writes all files atomically via the Git tree API (single commit) +4. Also writes `config.json` for backward compatibility -Deploy a test stack directly: +## Setting Up Service Catalog -```bash -aws cloudformation create-stack \ - --stack-name test-repo-creation \ - --template-body file://cloudformation-template.yaml \ - --parameters \ - ParameterKey=ProjectName,ParameterValue=test-repo-$(date +%s) \ - ParameterKey=OwningTeam,ParameterValue=platform-team \ - ParameterKey=Environment,ParameterValue=development \ - --region us-gov-west-1 -``` +### Using Terraform (Recommended) -Monitor the stack: +The `deploy/service_catalog.tf` file handles everything when `create_service_catalog = true`: ```bash -aws cloudformation describe-stack-events \ - --stack-name test-repo-creation \ - --region us-gov-west-1 +cd deploy/ +terraform apply ``` -### Test 3: Service Catalog - -1. Go to **AWS Service Catalog** → **Products** -2. Find your "Create GitHub Repository" product -3. Click **Launch product** -4. Fill in the parameters -5. Click **Launch** -6. Monitor the provisioned product status -7. Check the **Outputs** tab for repository URLs +This creates: +1. **Portfolio** — `github-automation-github-automation` +2. **Product** — `github-automation-github-repo-creator` with template from `service-catalog/product-template.yaml` +3. **S3 Upload** — template uploaded with `servicecatalog:provisioning = true` tag (required by SCPs) +4. **Launch Constraint** — `github-automation-sc-launch-role` assumed by CloudFormation +5. **Template Constraint** — locks `LambdaFunctionArn` to the deployed function +6. **Principal Association** — grants specified IAM roles access to provision -## Request Types +### Using AWS Console -The Lambda handles three CloudFormation request types: +1. Go to **AWS Service Catalog** → **Products** → **Create product** +2. Product name: `EKS Repository Creator` +3. Upload `service-catalog/product-template.yaml` +4. Add to a portfolio, grant access to IAM roles -### Create +## Launching a Product -Creates a new repository with configuration. +### Via Console -```json -{ - "RequestType": "Create", - "ResourceProperties": { - "ProjectName": "new-repo", - "OwningTeam": "platform-team" - } -} -``` +1. **Service Catalog** → **Products** → click the product +2. **Launch product** → fill in all parameters +3. Wait for **Available** status (30–60 seconds) +4. **Outputs** tab shows `RepositoryUrl` and `PullRequestUrl` -### Update +### Via CLI -Currently treated the same as Create. Future enhancement: update repository configuration. - -```json -{ - "RequestType": "Update", - "ResourceProperties": { - "ProjectName": "existing-repo", - "Environment": "production" - } -} +```bash +aws servicecatalog provision-product \ + --product-id prod-w3uvfaxmeblxe \ + --provisioning-artifact-name "v1.0" \ + --provisioned-product-name "my-eks-cluster" \ + --provisioning-parameters \ + Key=ProjectName,Value=my-eks-cluster \ + Key=AccountName,Value=csvd-dev-ew \ + Key=AWSAccountId,Value=229685449397 \ + Key=EnvironmentAbbr,Value=dev \ + Key=VpcName,Value=csvd-dev-vpc \ + Key=VpcDomainName,Value=dev.inf.csp1.census.gov \ + --region us-gov-west-1 ``` -### Delete +### Via Test Script (Bypasses Service Catalog) -Acknowledged but does not delete the repository (manual cleanup required). +```bash +# EKS deployment test +python scripts/test_workflow.py --eks -```json -{ - "RequestType": "Delete", - "PhysicalResourceId": "new-repo-repository" -} +# Generic test +python scripts/test_workflow.py ``` -**Why?** Repositories contain code and history that shouldn't be automatically deleted. - -## Parameter Handling +## Request Types -### Parameter Name Conversion +### Create +Creates a new repository with configuration. This is the primary operation. -CloudFormation uses PascalCase, but your config files need snake_case: +### Update +Currently treated the same as Create. Future: update repository configuration. -| CloudFormation | Lambda Converts To | -|----------------|-------------------| -| `ProjectName` | `project_name` | -| `OwningTeam` | `owning_team` | -| `AwsRegion` | `aws_region` | -| `MyCustomParameter` | `my_custom_parameter` | +### Delete +Acknowledged but **does not delete the repository**. Repositories contain code and history that shouldn't be automatically deleted. -### Dynamic Parameters +## Available Output Attributes -ALL parameters (except `ServiceToken`) are stored in the repository's `config.json`: - -```yaml -# In CloudFormation -Properties: - ServiceToken: !Ref LambdaArn - ProjectName: my-app - Environment: production - AwsRegion: us-gov-west-1 - CustomField: some-value -``` - -Results in `config.json`: - -```json -{ - "attrs": { - "environment": "production", - "aws_region": "us-gov-west-1", - "custom_field": "some-value" - }, - "tags": {} -} -``` +| Attribute | Description | +|-----------|-------------| +| `repository_url` | URL of the created repository | +| `pull_request_url` | URL of the configuration PR (GitHub) | +| `branch_name` | Name of the config branch (`repo-init`) | +| `RepositoryUrl` | Alias (backward compatibility) | +| `RepositoryName` | Name of the repository | +| `PullRequestUrl` | Alias (backward compatibility) | +| `MergeRequestUrl` | Merge request URL (GitLab) | ## Monitoring and Debugging ### CloudWatch Logs -Lambda logs are in CloudWatch Logs: - ```bash -aws logs tail /aws/lambda/service-catalog-repo-gen-template-automation \ - --follow \ - --region us-gov-west-1 +aws logs tail /aws/lambda/eks-terragrunt-repo-gen-template-automation \ + --follow --region us-gov-west-1 ``` ### CloudFormation Events -View stack events to see Custom Resource status: - ```bash aws cloudformation describe-stack-events \ - --stack-name your-stack-name \ - --region us-gov-west-1 + --stack-name your-stack-name --region us-gov-west-1 ``` ### Common Issues -#### 1. Lambda not invoked - -**Symptom**: CloudFormation stuck at "CREATE_IN_PROGRESS" - -**Solution**: Check Lambda permissions - CloudFormation needs `lambda:InvokeFunction` permission +#### 1. Stack stuck at CREATE_IN_PROGRESS +Lambda never invoked — check CloudFormation has permission: ```bash aws lambda get-policy \ - --function-name service-catalog-repo-gen-template-automation \ + --function-name eks-terragrunt-repo-gen-template-automation \ --region us-gov-west-1 ``` -#### 2. Repository not created - -**Symptom**: CloudFormation fails with custom resource error - -**Solution**: Check Lambda CloudWatch logs for the actual error +#### 2. 403 Forbidden on repo creation +GHE enterprise policy blocks private repos. Set `REPO_VISIBILITY=internal` on the Lambda: ```bash -aws logs filter-log-events \ - --log-group-name /aws/lambda/service-catalog-repo-gen-template-automation \ - --filter-pattern "ERROR" \ +aws lambda update-function-configuration \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --environment "Variables={...,REPO_VISIBILITY=internal}" \ --region us-gov-west-1 ``` -#### 3. Missing outputs - -**Symptom**: Stack outputs show "N/A" or empty values +#### 3. SSL certificate errors -**Solution**: Ensure you're using the correct attribute names with `!GetAtt` +Set `VERIFY_SSL=false` temporarily, or ensure the Census Root CA cert is baked into the container image (see `packer.pkr.hcl` shell provisioner). -Valid attributes: `RepositoryUrl`, `RepositoryName`, `PullRequestUrl`, `MergeRequestUrl` +#### 4. S3 Access Denied when launching product -## Best Practices - -### 1. Use Parameters for Validation - -Add constraints to CloudFormation parameters: - -```yaml -Parameters: - ProjectName: - Type: String - AllowedPattern: ^[a-zA-Z0-9-]+$ - MinLength: 3 - MaxLength: 100 - ConstraintDescription: Must contain only alphanumeric characters and hyphens -``` - -### 2. Store Important Values - -Use SSM Parameter Store to track created repositories: - -```yaml -Resources: - RepositoryParameter: - Type: AWS::SSM::Parameter - Properties: - Name: !Sub '/repositories/${ProjectName}' - Type: String - Value: !GetAtt GitHubRepository.RepositoryUrl -``` +The S3 object must have `servicecatalog:provisioning = true` tag. See `docs/SERVICE_CATALOG_RESOLUTION.md`. -### 3. Add Metadata for Better UI +#### 5. Missing or empty stack outputs -Use CloudFormation Metadata to improve Service Catalog UI: +Ensure you use the correct attribute names: `repository_url`, `pull_request_url`. The Lambda returns both camelCase and snake_case variants. -```yaml -Metadata: - AWS::CloudFormation::Interface: - ParameterGroups: - - Label: - default: "Repository Configuration" - Parameters: - - ProjectName - - OwningTeam - ParameterLabels: - ProjectName: - default: "Repository Name" -``` - -### 4. Use Conditions for Optional Features - -```yaml -Conditions: - CreateSSMParameter: !Not [!Equals [!Ref Environment, "development"]] - -Resources: - RepositoryParameter: - Type: AWS::SSM::Parameter - Condition: CreateSSMParameter - Properties: - Name: !Sub '/repositories/${ProjectName}' - Value: !GetAtt GitHubRepository.RepositoryUrl -``` - -## Advanced: Multiple Template Repositories - -To support multiple template types, add a parameter: - -```yaml -Parameters: - RepositoryType: - Type: String - Description: Type of repository template to use - AllowedValues: - - eks-cluster - - lambda-function - - terraform-module - -Resources: - GitHubRepository: - Type: Custom::RepositoryCreator - Properties: - ServiceToken: !Ref LambdaFunctionArn - ProjectName: !Ref ProjectName - TemplateType: !Ref RepositoryType -``` - -Then update Lambda environment variables or use different Lambda functions per template type. - -## Summary +## Best Practices -1. ✅ Deploy Lambda using Terraform in `deploy/` -2. ✅ Create CloudFormation template with Custom Resource -3. ✅ Create Service Catalog product from template -4. ✅ Grant users access to the product -5. ✅ Users provision products through Service Catalog UI -6. ✅ Lambda creates repositories automatically -7. ✅ Stack outputs show repository URLs +1. **Use the EKS product template** (`service-catalog/product-template.yaml`) with snake_case property names +2. **Always set `REPO_VISIBILITY=internal`** for GHE Enterprise environments +3. **Tag S3 objects** with `servicecatalog:provisioning = true` for SCP compliance +4. **Use template constraints** to lock `LambdaFunctionArn` — prevents users from pointing to arbitrary functions +5. **Monitor CloudWatch** after changes — the Lambda produces verbose structured logs diff --git a/CLOUDFORMATION_CUSTOM_RESOURCE_MIGRATION.md b/CLOUDFORMATION_CUSTOM_RESOURCE_MIGRATION.md index 61a0878e..dcd2476a 100644 --- a/CLOUDFORMATION_CUSTOM_RESOURCE_MIGRATION.md +++ b/CLOUDFORMATION_CUSTOM_RESOURCE_MIGRATION.md @@ -129,7 +129,7 @@ New file: `events/cloudformation-create-event.json` ```bash aws lambda invoke \ - --function-name service-catalog-repo-gen-template-automation \ + --function-name eks-terragrunt-repo-gen-template-automation \ --payload file://events/cloudformation-create-event.json \ response.json ``` @@ -244,20 +244,22 @@ Sees outputs: ## Deployment Checklist -- [ ] Build new Lambda container with updated code -- [ ] Push container to ECR -- [ ] Apply Terraform changes in `deploy/` -- [ ] Verify Lambda permission for CloudFormation -- [ ] Upload CloudFormation template to S3 -- [ ] Create/update Service Catalog product -- [ ] Test with a sample repository -- [ ] Verify outputs in CloudFormation stack -- [ ] Check created repository has config.json -- [ ] Verify pull request was created +- [x] Build new Lambda container with updated code +- [x] Push container to ECR +- [x] Apply Terraform changes in `deploy/` +- [x] Verify Lambda permission for CloudFormation +- [x] Upload CloudFormation template to S3 +- [x] Create/update Service Catalog product +- [x] Test with a sample repository (generic mode) +- [x] Test with EKS deployment mode (`python scripts/test_workflow.py --eks`) +- [x] Verify outputs in CloudFormation stack +- [x] Check created repository has config.json +- [x] Check created repository has rendered HCL files (EKS mode) +- [x] Verify pull request was created ## Support For issues or questions: -1. Check Lambda CloudWatch logs: `/aws/lambda/service-catalog-repo-gen-template-automation` +1. Check Lambda CloudWatch logs: `/aws/lambda/eks-terragrunt-repo-gen-template-automation` 2. Review CloudFormation stack events 3. See `CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md` for troubleshooting diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 8e3e3af3..693ce4fc 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -1,192 +1,267 @@ -# Deployment Guide: Service Catalog Repository Generator +# Deployment Guide -This guide walks through deploying the Lambda function that Service Catalog will invoke (via CloudFormation Custom Resource) to create GitHub repositories. +This guide walks through deploying the Service Catalog Repository Generator — the Lambda function, supporting infrastructure, and the Service Catalog product that end-users interact with. -## Overview +## Prerequisites -The deployment has three main stages: -1. **Create ECR Repository** - Where the Lambda container image will be stored -2. **Build & Push Container** - Build the Lambda code into a container and push to ECR -3. **Deploy Lambda Function** - Deploy the Lambda function using Terraform +- AWS CLI configured with GovCloud credentials (`source ~/aws-creds`) +- Terraform >= 1.0 +- `packer-pipeline` CLI installed (`pip install -e /path/to/packer-pipeline`) +- GitHub PAT with `repo`, `admin:org`, `read:org` scopes stored in Secrets Manager +- S3 buckets: + - `csvd-template-automation-builds` (build artifacts) + - `image-pipeline-assets-dev` (Packer binary assets) + - `servicecatalog-product-artifacts-*` (Service Catalog templates) -## Prerequisites +## Architecture Overview -- AWS CLI configured with appropriate credentials -- Terraform installed (>= 1.0) -- `packer-pipeline` tool installed -- GitHub/GitLab personal access token stored in AWS Secrets Manager -- S3 buckets created: - - `csvd-template-automation-builds` (for build artifacts) - - `image-pipeline-assets-dev` (for Packer binaries) +``` + ┌────────────────────────────┐ + │ Service Catalog Console │ + │ (End-User launches here) │ + └────────────┬───────────────┘ + │ + ┌────────────▼───────────────┐ + │ CloudFormation │ + │ Custom::GitHubRepository │ + └────────────┬───────────────┘ + │ Invokes + ┌────────────▼───────────────┐ + │ Lambda Function │ + │ (ECR container image) │ + │ ┌──────────────────────┐ │ + │ │ template_automation/ │ │ + │ │ app.py (handler) │ │ + │ │ eks_config.py │ │ + │ │ github_provider.py │ │ + │ │ templates/eks/*.j2 │ │ + │ └──────────────────────┘ │ + └────────────┬───────────────┘ + │ Creates + ┌────────────▼───────────────┐ + │ GitHub Enterprise │ + │ - New repository │ + │ - 8 Terragrunt HCL files │ + │ - Pull request │ + └────────────────────────────┘ +``` ## Step 1: Create ECR Repository (Infrastructure) ```bash -cd /home/a/arnol377/git/lambda-template-repo-generator +cd /path/to/lambda-template-repo-generator -# Review the ECR repository configuration terraform init -terraform plan - -# Create the ECR repository -terraform apply +terraform plan -var-file=varfiles/default.tfvars +terraform apply -var-file=varfiles/default.tfvars ``` -**Output**: ECR repository at `229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/service-catalog-repo-generator/lambda` +**Output**: ECR repository at `229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda` -## Step 2: Build and Push Lambda Container (Pipeline) +## Step 2: Build and Push Lambda Container ```bash -cd /home/a/arnol377/git/lambda-template-repo-generator +cd /path/to/lambda-template-repo-generator -# Build using packer-pipeline (this creates CodeBuild project and runs the build) -packer-pipeline build --config config_packer.hcl +# Build container image via CodeBuild (waits for completion, ~4 minutes) +packer-pipeline --config config_packer.hcl --wait ``` This will: -- Create a CodeBuild project named `service-catalog-repo-generator-builder` -- Package your code and upload to S3 -- Run Packer to build the Docker image -- Push the image to ECR with tag `latest` +- Zip 91 source files and upload to S3 +- Clone `public.ecr.aws/lambda/python:3.11` to private ECR (via skopeo) +- Create/update CodeBuild project `eks-terragrunt-repo-generator-builder` +- Run Packer to build the Docker image (installs deps, copies code, installs CA cert) +- Push to ECR with tag `latest` + +**After the build completes**, update the Lambda to use the new image: + +```bash +aws lambda update-function-code \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --image-uri 229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda:latest \ + --region us-gov-west-1 + +# Wait for the update to complete +aws lambda wait function-updated \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --region us-gov-west-1 +``` **Verify the build**: ```bash -# Check the ECR repository for the image aws ecr describe-images \ - --repository-name service-catalog-repo-generator/lambda \ + --repository-name eks-terragrunt-repo-generator/lambda \ --region us-gov-west-1 ``` -## Step 3: Deploy Lambda Function (Application) +## Step 3: Deploy Lambda + Service Catalog ### 3a. Configure Deployment Variables ```bash -cd /home/a/arnol377/git/lambda-template-repo-generator/deploy - -# Copy the example tfvars file -cp terraform.tfvars.example terraform.tfvars - -# Edit with your values -nano terraform.tfvars +cd deploy/ ``` -Update the following values: -- `github_org_name` - Your GitHub organization -- `template_repo_name` - The template repository to clone from -- `github_token_secret_name` - Name of your Secrets Manager secret -- VPC settings if using GitHub Enterprise +Edit `terraform.tfvars` with your values. Key settings: + +```hcl +aws_region = "us-gov-west-1" +github_api_url = "https://github.e.it.census.gov" # code adds /api/v3 automatically +github_org_name = "SCT-Engineering" +template_repo_name = "template-eks-cluster" +repo_visibility = "internal" # GHE enterprise policy blocks 'private' +github_token_secret_name = "/eks-cluster-deployment/github_token" +create_service_catalog = true +enable_vpc = true +subnet_ids = ["subnet-0b1992a84536c581b"] +security_group_ids = ["sg-0641c697588b9aa6b"] + +service_catalog_config = { + artifacts_bucket_name = "servicecatalog-product-artifacts-20250904021619588100000003" + principal_arns = [ + "arn:aws-us-gov:iam::229685449397:role/aws-reserved/sso.amazonaws.com/us-gov-east-1/AWSReservedSSO_inf-admin-t2_4e0c6446aecbe4a0" + ] +} +``` ### 3b. Deploy Infrastructure ```bash -# Initialize Terraform terraform init - -# Review the plan terraform plan - -# Deploy the Lambda function and CloudFormation permissions terraform apply ``` This creates: -- ✅ Lambda function using your container image -- ✅ IAM roles and policies -- ✅ SSM parameters for configuration -- ✅ Lambda permissions for CloudFormation to invoke it (as Custom Resource) +- ✅ Lambda function using ECR container image (VPC-attached) +- ✅ IAM roles and policies (SSM, Secrets Manager, KMS, VPC, CloudWatch) +- ✅ Lambda permission for CloudFormation to invoke as Custom Resource +- ✅ API Gateway HTTP endpoint (alternative invocation method) +- ✅ CloudWatch Log Group (`/aws/lambda/eks-terragrunt-repo-gen-template-automation`) +- ✅ **Service Catalog Portfolio** — `github-automation-github-automation` +- ✅ **Service Catalog Product** — `github-automation-github-repo-creator` +- ✅ **Launch Constraint** — role assumed by CloudFormation when launching the product +- ✅ **Template Constraint** — locks `LambdaFunctionArn` to prevent users from changing it +- ✅ S3 upload of `product-template.yaml` with `servicecatalog:provisioning = true` tag + +Note the outputs: +``` +lambda_function_arn = "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:eks-terragrunt-repo-gen-template-automation" +service_catalog_product_id = "prod-w3uvfaxmeblxe" +service_catalog_provisioning_url = "https://console.amazonaws-us-gov.com/servicecatalog/home?region=us-gov-west-1#/products/prod-w3uvfaxmeblxe" +``` -**Important**: Note the `lambda_function_arn` output. You will need this for your Service Catalog CloudFormation template. +## Step 4: Launch the Product in Service Catalog + +### Via AWS Console + +1. Navigate to **AWS Service Catalog** → **Products** +2. Find **github-automation-github-repo-creator** +3. Click **Launch product** +4. Enter a **Provisioned product name** (e.g., `my-eks-cluster-repo`) +5. Fill in the EKS parameters: + - **Repository Name** (`ProjectName`): `my-eks-cluster` (lowercase, hyphens) + - **EKS Cluster Name** (`ClusterName`): leave blank to default to ProjectName + - **Environment**: `dev` / `test` / `prod` + - **AWS Region**: `us-gov-west-1` + - **Account Name**: e.g., `csvd-dev-ew` + - **AWS Account ID**: 12-digit account ID + - **VPC Name**: e.g., `csvd-dev-vpc` + - **VPC Domain Name**: e.g., `dev.inf.csp1.census.gov` + - (optional) **Owning Team**, **Mailing List**, **FinOps** fields +6. Click **Launch product** +7. Wait for status to show **Available** (30–60 seconds) +8. Click the provisioned product → **Outputs**: + - **RepositoryUrl**: `https://github.e.it.census.gov/SCT-Engineering/my-eks-cluster` + - **PullRequestUrl**: `https://github.e.it.census.gov/SCT-Engineering/my-eks-cluster/pull/1` +9. Go to the PR on GitHub — review the rendered HCL files, then merge + +### Via AWS CLI -## Step 4: Create Service Catalog Product +```bash +aws servicecatalog provision-product \ + --product-id prod-w3uvfaxmeblxe \ + --provisioning-artifact-name "v1.0" \ + --provisioned-product-name "my-eks-test" \ + --provisioning-parameters \ + Key=ProjectName,Value=my-eks-test \ + Key=AccountName,Value=csvd-dev-ew \ + Key=AWSAccountId,Value=229685449397 \ + Key=EnvironmentAbbr,Value=dev \ + Key=VpcName,Value=csvd-dev-vpc \ + Key=VpcDomainName,Value=dev.inf.csp1.census.gov \ + --region us-gov-west-1 +``` -Now you need to create a Service Catalog product that uses the Lambda as a CloudFormation Custom Resource. +## Step 5: Validate with Test Script -### Use the Provided Template +Instead of Service Catalog, you can test the Lambda directly using the test script: -A complete CloudFormation template is available at `cloudformation-template.yaml`. +```bash +# EKS deployment test (recommended) +cd /path/to/lambda-template-repo-generator +python scripts/test_workflow.py --eks -1. **Update the Template**: ensuring the `ServiceToken` points to your deployed Lambda ARN. -2. **Upload to Service Catalog**: Create a new product of type "CloudFormation Template" and upload this file. +# Generic (non-EKS) test +python scripts/test_workflow.py +``` -### Custom Resource Definition +This bypasses Service Catalog and directly invokes the Lambda with a synthetic CloudFormation event. See [README.md](README.md#testing) for full details. -If you are writing your own template, the resource definition looks like this: +## Monitoring and Troubleshooting -```yaml -Resources: - TriggerLambda: - Type: Custom::RepositoryCreator - Properties: - ServiceToken: !Sub 'arn:aws-us-gov:lambda:${AWS::Region}:${AWS::AccountId}:function:service-catalog-repo-gen-template-automation' - ProjectName: !Ref ProjectName - OwningTeam: !Ref OwningTeam - Environment: !Ref Environment - # Add any other parameters you need -``` +### Check Lambda Logs -## Step 5: Test the Integration +```bash +aws logs tail /aws/lambda/eks-terragrunt-repo-gen-template-automation \ + --follow --region us-gov-west-1 +``` -### Manual Lambda Test +### Check Service Catalog Provisioned Product Status ```bash -# Invoke Lambda directly with a Custom Resource test event -aws lambda invoke \ - --function-name service-catalog-repo-gen-template-automation \ - --payload file://events/cloudformation-create-event.json \ - --region us-gov-west-1 \ - response.json - -cat response.json +aws servicecatalog describe-provisioned-product \ + --name "my-eks-test" --region us-gov-west-1 ``` -### Service Catalog Test +### Common Issues -1. Go to AWS Service Catalog console -2. Provision the product you created in Step 4 -3. Wait for the stack to complete (the Lambda runs synchronously) -4. Check the "Outputs" tab of the provisioned product for the Repository URL. +| Problem | Symptom | Solution | +|---------|---------|---------| +| Stack stuck at CREATE_IN_PROGRESS | Lambda never invoked | Check `aws lambda get-policy` for CloudFormation permission | +| 403 on repo creation | GHE blocks private repos | Set `REPO_VISIBILITY=internal` on Lambda | +| SSL errors | `SSLCertVerificationError` | Set `VERIFY_SSL=false` or bake CA cert into container | +| 401 Bad credentials | Expired PAT | Rotate token in Secrets Manager | +| S3 Access Denied on launch | Missing tag on S3 object | Ensure `servicecatalog:provisioning = true` tag (see `docs/SERVICE_CATALOG_RESOLUTION.md`) | +| Stack shows N/A outputs | Wrong `!GetAtt` attribute names | Use `repository_url`, `pull_request_url`, `branch_name` | -## Monitoring and Troubleshooting - -### Check Lambda Logs +### Verify Permissions ```bash -# Get recent logs -aws logs tail /aws/lambda/service-catalog-repo-gen-template-automation \ - --follow \ - --region us-gov-west-1 +python scripts/check_github_permissions.py ``` -### Response Handling +## Rebuild Cycle (Code Changes) -The Lambda must send a response back to CloudFormation (using the pre-signed URL in the event) for the stack to proceed. If the Lambda times out or crashes before sending a response, the stack will hang until the timeout (usually 1 hour). +When you change Lambda code in `template_automation/`: -If your stack is stuck: -1. Check Lambda logs for errors -2. Manually signal failure if needed using `curl` to the ResponseURL found in the CloudWatch logs. - -## Architecture Diagram +```bash +# 1. Build new container +packer-pipeline --config config_packer.hcl --wait -``` -User → Service Catalog UI - ↓ - Provisions Product - ↓ - CloudFormation Stack Creates - ↓ - Custom Resource Invokes Lambda - ↓ - Lambda Creates GitHub Repository - ↓ - Lambda Responds SUCCESS to CloudFormation - ↓ - Stack Completes with Outputs -``` +# 2. Update Lambda to new image +aws lambda update-function-code \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --image-uri 229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda:latest \ + --region us-gov-west-1 -## Next Steps +# 3. Wait for update +aws lambda wait function-updated \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --region us-gov-west-1 -1. Create Service Catalog portfolio and products -2. Set up proper IAM permissions for users to provision products -3. Add constraints to Service Catalog product versions +# 4. Test +python scripts/test_workflow.py --eks +``` diff --git a/Dockerfile b/Dockerfile index 142192f8..04b93bd4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,14 @@ # DEPRECATED: This Dockerfile is not used for deployment. # Lambda container image is built using Packer (see packer.pkr.hcl) # ---------------------------------------------------------------- -# Service Catalog Repository Generator Lambda +# EKS Terragrunt Repository Generator Lambda # -# This Lambda function processes AWS Service Catalog provisioning events -# to automatically create and configure GitHub/GitLab repositories. +# This Lambda function processes CloudFormation Custom Resource events +# from AWS Service Catalog to create and configure GitHub repositories +# with rendered EKS Terragrunt/HCL configuration files. # -# Build method: Packer (packer.pkr.hcl) -# Event source: AWS EventBridge (Service Catalog events) +# Build method: packer-pipeline --config config_packer.hcl --wait +# Event source: CloudFormation Custom Resource (Custom::GitHubRepository) # ---------------------------------------------------------------- # Keeping this file for reference only diff --git a/PACKER_UPDATES.md b/PACKER_UPDATES.md index 00c9b246..37045964 100644 --- a/PACKER_UPDATES.md +++ b/PACKER_UPDATES.md @@ -1,103 +1,108 @@ -# Packer Configuration Updates for Repository Generator Lambda - -## Summary of Changes - -The Packer configuration has been updated to build the Lambda container image that processes **CloudFormation Custom Resource** events for Service Catalog repository creation. - -## Files Modified - -### 1. `packer.pkr.hcl` - -**Changes:** -- Added comprehensive header comment explaining Service Catalog integration -- Updated build name from `template-automation-lambda` to `service-catalog-repo-generator` -- Updated CMD to use `template_automation.app.lambda_handler` instead of `app.lambda_handler` -- Enhanced variable descriptions to reference Service Catalog purpose -- Added installation messages in shell provisioner for clarity - -**Key Updates:** -```hcl -# Build name -build { - name = "service-catalog-repo-generator" - ... -} - -# Lambda handler -source "docker" "lambda" { - ... - changes = [ - "WORKDIR /var/task", - "CMD [ \"template_automation.app.lambda_handler\" ]" - ] -} -``` +# Packer Configuration — Container Build Details + +## Overview -### 2. `config_packer.hcl` +The Packer configuration builds the Lambda container image that processes **CloudFormation Custom Resource** events for Service Catalog EKS repository creation. -**Changes:** -- Updated header comment to reference Service Catalog Repository Generator -- Changed CodeBuild project name from `template-automation-lambda-builder` to `service-catalog-repo-generator-builder` -- Updated S3 key prefix from `packer-builds/template-automation-lambda` to `packer-builds/service-catalog-repo-generator` +## Build Process -**Key Updates:** -```hcl -codebuild_project_name = "service-catalog-repo-generator-builder" -s3_key_prefix = "packer-builds/service-catalog-repo-generator" +``` +config_packer.hcl packer.pkr.hcl ECR +───────────────── ─────────────── ─── + CodeBuild project name Base: lambda/python:3.11 229685449397.dkr.ecr.us-gov-west-1. + S3 bucket / key prefix + requirements.txt amazonaws.com/service-catalog-repo- + VPC / compute settings + template_automation/ generator/lambda:latest + Exclude dirs + Census Root CA cert + + pip config + │ │ + ▼ ▼ + packer-pipeline CLI CodeBuild → Packer → Docker → ECR push ``` -### 3. `Dockerfile` +## Key Files -**Changes:** -- Added Service Catalog context to header comments -- Updated CMD to use `template_automation.app.lambda_handler` -- Added explanation that this is for Service Catalog event processing +### `config_packer.hcl` -## Build Process +Configures the `packer-pipeline` CLI: + +| Setting | Value | +|---------|-------| +| `codebuild_project_name` | `eks-terragrunt-repo-generator-builder` | +| `s3_bucket` | `csvd-template-automation-builds` | +| `s3_key_prefix` | `packer-builds/eks-terragrunt-repo-generator` | +| `packer_template_file` | `packer.pkr.hcl` | +| `compute_type` | `BUILD_GENERAL1_MEDIUM` | +| `account_number` | `229685449397` | +| `partition` | `aws-us-gov` | +| `aws_region` | `us-gov-west-1` | + +### `packer.pkr.hcl` -The Packer template builds a Lambda container image with these characteristics: +Builds the Docker image: -1. **Base Image**: AWS Lambda Python 3.11 (`public.ecr.aws/lambda/python:3.11`) -2. **Handler**: `template_automation.app.lambda_handler` -3. **Dependencies**: Installed from `requirements.txt` including: - - pydantic (v2) for CloudFormation parameter validation - - boto3 for AWS service integration - - requests for GitHub/GitLab API calls +- **Base image**: `229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda-python:3.11` (cloned from `public.ecr.aws/lambda/python:3.11`) +- **Handler**: `template_automation.app.lambda_handler` +- **Build name**: `eks-terragrunt-repo-generator` +- **Shell provisioner** installs: + - Python dependencies from `requirements.txt` (pydantic v1, jinja2, requests, boto3, etc.) + - Census Bureau Root CA certificate into `/etc/pki/ca-trust/extracted/` + - Copies `template_automation/` package to `/var/task` + - Copies `pip.conf` and `pip-cert.pem` for enterprise pip registry + +### `terraform_version` Variable + +The Packer template includes a `terraform_version` variable that is **not used** for Lambda builds. It exists because `packer-pipeline`'s buildspec template always passes this variable (designed for both Terraform module builds and Lambda builds). See `PACKER_PIPELINE_EXPLANATION.md` for details. ## Build Command ```bash -packer build \ - -var "repository_uri=" \ - -var "tag=" \ - -var "ecr_login_username=AWS" \ - -var "ecr_login_password=" \ - -var "ecr_login_server=" \ - packer.pkr.hcl -``` +# Full build (creates CodeBuild project, uploads source, builds, pushes to ECR) +packer-pipeline --config config_packer.hcl --wait -## Deployment Integration +# Dry run (validates configuration without building) +packer-pipeline --config config_packer.hcl --dry-run +``` -The built container image is designed to be: -1. **Triggered by**: CloudFormation via Custom Resource -2. **Event format**: CloudFormation Custom Resource events (Create, Update, Delete) -3. **Output**: Creates GitHub/GitLab repositories with configuration from CloudFormation parameters +## After Building -## Validation +Update the Lambda to use the new image: -Packer template validation confirmed: ```bash -$ packer validate packer.pkr.hcl -The configuration is valid. +aws lambda update-function-code \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --image-uri 229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda:latest \ + --region us-gov-west-1 + +aws lambda wait function-updated \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --region us-gov-west-1 ``` -## Next Steps +## Dependencies Installed -To deploy the Lambda function: +Key Python packages in the container (from `requirements.txt`): + +| Package | Version | Purpose | +|---------|---------|---------| +| pydantic | 1.10.24 | CloudFormation parameter validation (v1, not v2) | +| jinja2 | 3.x | EKS Terragrunt template rendering | +| requests | 2.x | GitHub/GitLab API calls | +| boto3 | 1.x | AWS SDK (Secrets Manager, SSM, CloudWatch) | +| PyGithub | 2.x | GitHub API helper | + +## Environment Variables Required -1. Build the container image using Packer -2. Push to ECR (automated by Packer post-processor) -3. Create Lambda function using the container image +The container expects these at runtime (set via Lambda configuration): + +| Variable | Description | +|----------|-------------| +| `GITHUB_API` | GitHub Enterprise API URL | +| `GITHUB_TOKEN_SECRET_NAME` | Secrets Manager secret path | +| `GITHUB_ORG_NAME` | GitHub organization | +| `TEMPLATE_REPO_NAME` | Template repository name | +| `REPO_VISIBILITY` | `internal` / `private` / `public` | +| `VERIFY_SSL` | `true` / `false` | +| `PARAM_STORE_PREFIX` | SSM parameter prefix | 4. Grant CloudFormation permission to invoke the Lambda 5. Create a Service Catalog product using `cloudformation-template.yaml` 6. Set environment variables for GitHub/GitLab integration diff --git a/README.md b/README.md index 8ffe603f..97a58990 100644 --- a/README.md +++ b/README.md @@ -1,259 +1,382 @@ -# Lambda Template Repository Generator (CloudFormation Custom Resource) +# Lambda Template Repository Generator ## Overview -This Lambda function automates the creation and configuration of new repositories from templates using **CloudFormation Custom Resources**. When invoked as a Custom Resource in a CloudFormation stack (typically through AWS Service Catalog), this Lambda function automatically creates a GitHub/GitLab repository with the appropriate configuration. +This Lambda function automates the creation and configuration of new GitHub repositories from templates using **AWS Service Catalog** and **CloudFormation Custom Resources**. When a user launches the product in Service Catalog, CloudFormation invokes this Lambda, which creates a new repository, clones a template, renders configuration files, and opens a pull request — all in a single provisioning operation. -This implementation uses CloudFormation Custom Resources to enable Service Catalog integration — the Lambda is invoked directly by CloudFormation during stack provisioning. +The system supports two deployment modes: + +- **Generic mode** — writes a single `config.json` with all CloudFormation parameters +- **EKS deployment mode** — renders a full Terragrunt/HCL file hierarchy (8 files) using Jinja2 templates, providing a production-ready EKS cluster configuration repository ## Key Features -- **CloudFormation Native**: Works as a CloudFormation Custom Resource -- **Service Catalog Compatible**: Perfect for Service Catalog product definitions -- **Input Validation**: Uses Pydantic for parameter validation -- **Dynamic Parameters**: Accepts ANY parameters and stores them in config.json -- **Multi-Provider**: Supports both GitHub and GitLab -- **Automatic PR Creation**: Creates pull request with configuration +- **CloudFormation Native** — works as a CloudFormation Custom Resource (`Custom::GitHubRepository`) +- **Service Catalog Integration** — users launch from the AWS console, get outputs (repo URL, PR URL) +- **EKS Config Rendering** — renders 8 Terragrunt HCL files (root.hcl, account.hcl, cluster.hcl, etc.) with 60+ version pins +- **Atomic Multi-File Commits** — uses the Git tree API to write all files in a single commit +- **Input Validation** — Pydantic v1 models for parameter validation +- **Multi-Provider** — supports both GitHub Enterprise and GitLab +- **Automatic PR Creation** — opens a pull request with configuration ready for review ## Repository Structure ``` lambda-template-repo-generator/ -├── main.tf # Step 1: Terraform for ECR repository -├── variables.tf # ECR Terraform variables -├── backend.tf # Terraform state backend -├── varfiles/ # Terraform/Packer variable files -│ ├── default.tfvars # ECR Terraform vars -│ └── packer.pkrvars.hcl # Packer variable overrides -├── config_packer.hcl # Step 2: Packer Pipeline configuration -├── packer.pkr.hcl # Packer template (container image build) -├── deploy/ # Step 3: Lambda deployment Terraform -│ ├── main.tf # Uses terraform-aws-template-automation module -│ ├── variables.tf # Deployment variables -│ └── terraform.tfvars.example -├── template_automation/ # Lambda source code -│ ├── app.py # Lambda handler (CloudFormation events) -│ ├── github_provider.py # GitHub API provider -│ ├── gitlab_provider.py # GitLab API provider -│ ├── repository_provider.py # Abstract provider interface -│ ├── models.py # Pydantic models -│ └── template_manager.py # Jinja2 template rendering -├── tests/ # Unit and integration tests -├── cloudformation-template.yaml # Example Service Catalog product -├── requirements.txt # Python dependencies -└── Makefile # Development commands +├── main.tf # Step 1: Terraform for ECR repository +├── variables.tf # ECR Terraform variables +├── backend.tf # Terraform state backend +├── varfiles/ # Terraform/Packer variable files +├── config_packer.hcl # Step 2: Packer Pipeline configuration +├── packer.pkr.hcl # Packer template (container image build) +├── Dockerfile # Reference only (build uses Packer) +├── deploy/ # Step 3: Lambda + Service Catalog deployment +│ ├── main.tf # Uses terraform-aws-template-automation module +│ ├── variables.tf # Deployment variables +│ ├── service_catalog.tf # Portfolio, product, constraints, launch role +│ └── terraform.tfvars # Deployment configuration +├── service-catalog/ # CloudFormation product template +│ └── product-template.yaml # EKS-enabled Service Catalog product definition +├── cloudformation-template.yaml # Generic (non-EKS) product template example +├── template_automation/ # Lambda source code (Python package) +│ ├── app.py # Lambda handler (CloudFormation events) +│ ├── eks_config.py # EKS deployment Pydantic models + renderer +│ ├── github_provider.py # GitHub API provider (tree API, PRs, branches) +│ ├── gitlab_provider.py # GitLab API provider +│ ├── repository_provider.py # Abstract provider interface +│ ├── models.py # Shared Pydantic models +│ ├── template_manager.py # Jinja2 template rendering +│ └── templates/ +│ └── eks/ # 8 Jinja2 templates for EKS Terragrunt files +│ ├── root.hcl.j2 +│ ├── account.hcl.j2 +│ ├── region.hcl.j2 +│ ├── vpc.hcl.j2 +│ ├── cluster.hcl.j2 +│ ├── common-variables.hcl.j2 +│ ├── default-versions.hcl.j2 +│ └── README.md.j2 +├── scripts/ # Operational / test scripts +│ ├── test_workflow.py # End-to-end Lambda workflow tester +│ ├── check_github_permissions.py# GitHub PAT permissions audit (11 checks) +│ ├── cleanup_test_repos.py # Delete temp-test-repo-* repos from GHE +│ ├── validate_github_token.py # Token retrieval + validation +│ └── lambda_setup.py # Container setup helper +├── tests/ # Unit and integration tests +│ ├── test_app.py +│ ├── test_github_client.py +│ └── integration/ +├── events/ # Test event payloads +│ └── cloudformation-create-event.json +├── requirements.txt # Python dependencies +└── Makefile # Development commands ``` +## Quick Start + +### Launching the Product in Service Catalog (End-User) + +1. Sign in to the **AWS GovCloud Console** → navigate to **Service Catalog** +2. Click **Products** in the left sidebar +3. Find **github-automation-github-repo-creator** and click **Launch product** +4. Fill in the parameters: + + | Parameter | Required | Example | Description | + |-----------|----------|---------|-------------| + | **ProjectName** | Yes | `my-eks-cluster` | Repository name (lowercase, hyphens) | + | **ClusterName** | No | `my-cluster` | EKS cluster name (defaults to ProjectName) | + | **Environment** | Yes | `dev` | `dev`, `test`, or `prod` | + | **AwsRegion** | Yes | `us-gov-west-1` | Target AWS region | + | **AccountName** | Yes | `csvd-dev-ew` | AWS account name | + | **AWSAccountId** | Yes | `229685449397` | 12-digit account ID | + | **EnvironmentAbbr** | Yes | `dev` | Short environment name | + | **VpcName** | Yes | `csvd-dev-vpc` | VPC name for the cluster | + | **VpcDomainName** | Yes | `dev.inf.csp1.census.gov` | VPC domain | + | **OwningTeam** | No | `tf-module-admins` | GitHub team with admin access | + | **ClusterMailingList** | No | `team@census.gov` | Notification email | + | **OrganizationPath** | No | `census:ocio:csvd` | Org hierarchy path | + | **FinOpsProjectName** | No | `EKS Platform` | FinOps project | + | **FinOpsProjectNumber** | No | `12345` | FinOps number | + +5. Click **Launch product** +6. Wait for the stack to reach **CREATE_COMPLETE** (typically 30–60 seconds) +7. Click the provisioned product → **Outputs** tab to find: + - **RepositoryUrl** — the new GitHub repository + - **PullRequestUrl** — PR with all configuration files ready for review + +### What Gets Created + +When the product launches with EKS parameters, the Lambda: + +1. Creates a new **internal** repository in the `SCT-Engineering` GitHub org +2. Clones the `template-eks-cluster` template into it +3. Renders **8 Terragrunt/HCL configuration files** from Jinja2 templates: + - `root.hcl` — Terragrunt root config with remote state and provider generation + - `account.hcl` — Account-level locals (account name, ID, org path) + - `region.hcl` — Region-level locals + - `vpc.hcl` — VPC configuration (name, domain) + - `cluster.hcl` — Cluster-specific settings (name, mailing list, FinOps tags) + - `common-variables.hcl` — Shared Terraform variable definitions + - `default-versions.hcl` — 60+ pinned versions for all EKS add-ons + - `README.md` — Auto-generated documentation +4. Writes all files **atomically** in a single Git commit (via Git tree API) +5. Also writes a `config.json` with all raw parameters for backward compatibility +6. Opens a **pull request** from `repo-init` → `main` +7. Assigns the owning team as repository admins + ## Build & Deploy Workflow -The deployment is a three-step process: +Deployment is a three-step process: ``` -Step 1: Create ECR Repository Step 2: Build Container Image Step 3: Deploy Lambda Function -───────────────────────────── ────────────────────────────── ────────────────────────────── - terraform apply packer-pipeline terraform apply - (root main.tf) (config_packer.hcl) (deploy/ directory) - │ │ │ - ▼ ▼ ▼ - ECR Repository Created CodeBuild Project Lambda Function - (stores container images) Builds Docker Image IAM Roles & Policies - Pushes to ECR API Gateway - SSM Parameters - CloudWatch Log Group - CloudFormation Permission +Step 1: Create ECR Repo Step 2: Build Container Step 3: Deploy Lambda + Service Catalog +──────────────────────── ───────────────────────── ──────────────────────────────────────── + terraform apply packer-pipeline terraform apply + (root main.tf) (config_packer.hcl) (deploy/ directory) + │ │ │ + ▼ ▼ ▼ + ECR Repository CodeBuild → Packer → Lambda Function + (container storage) Docker → ECR push Service Catalog Portfolio + Service Catalog Product + Launch Role + Constraints + IAM, CloudWatch, API Gateway ``` ### Step 1: Create the ECR Repository -The root-level Terraform configuration creates an ECR public repository to store the Lambda container image. - ```bash -# Initialize and apply the ECR Terraform cd lambda-template-repo-generator/ terraform init terraform apply -var-file=varfiles/default.tfvars ``` -This creates: -- An ECR public repository (`template-automation-lambda`) -- Outputs the `repository_uri` needed by the Packer build +Creates the ECR repository at `229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda`. -### Step 2: Build the Container Image with Packer Pipeline +### Step 2: Build the Container Image -The [packer-pipeline](https://github.e.it.census.gov/SCT-Engineering/packer-pipeline) utility builds the Lambda container image via AWS CodeBuild and pushes it to the ECR repository created in Step 1. +The [packer-pipeline](https://github.e.it.census.gov/SCT-Engineering/packer-pipeline) tool builds the Lambda container via CodeBuild: ```bash -# Run the packer pipeline to build and push the container image -python -m packer_pipeline --config config_packer.hcl +# Build and push to ECR (waits for completion) +packer-pipeline --config config_packer.hcl --wait ``` This: -1. Zips the repository source code and uploads it to S3 -2. Creates/updates an AWS CodeBuild project -3. CodeBuild runs the Packer template (`packer.pkr.hcl`) which: - - Starts from the `public.ecr.aws/lambda/python:3.11` base image - - Copies application code into `/var/task` +1. Zips the repository (91 files) and uploads to S3 +2. Clones the base image `public.ecr.aws/lambda/python:3.11` to private ECR +3. Creates/updates CodeBuild project `eks-terragrunt-repo-generator-builder` +4. CodeBuild runs the Packer template which: - Installs Python dependencies from `requirements.txt` - - Tags the image and pushes to the ECR repository + - Copies `template_automation/` package into `/var/task` + - Installs Census Bureau Root CA certificate for SSL + - Tags and pushes the image to ECR -The `config_packer.hcl` file contains all the build configuration including VPC settings, environment variables, and ECR clone configuration for the base image. +After the build, update the Lambda to use the new image: -### Step 3: Deploy the Lambda Function +```bash +aws lambda update-function-code \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --image-uri 229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda:latest \ + --region us-gov-west-1 +``` -The `deploy/` directory contains Terraform that uses the [terraform-aws-template-automation](https://github.com/HappyPathway/terraform-aws-template-automation) module to deploy the Lambda function and all supporting infrastructure. +### Step 3: Deploy Lambda + Service Catalog ```bash -# Copy and edit the example tfvars cd deploy/ -cp terraform.tfvars.example terraform.tfvars -# Edit terraform.tfvars with your values - -# Initialize and apply +# Edit terraform.tfvars with your values (see deploy/terraform.tfvars for reference) terraform init terraform apply ``` This deploys: -- **Lambda Function** — container image from ECR, with configurable memory/timeout/VPC -- **IAM Role & Policies** — permissions for SSM, Secrets Manager, KMS, CloudWatch, VPC +- **Lambda Function** — container image from ECR, VPC-attached, 512 MB, 5 min timeout +- **IAM Role & Policies** — SSM, Secrets Manager, KMS, CloudWatch, VPC +- **Service Catalog Portfolio & Product** — with launch role and template constraint +- **Launch Constraint** — dedicated role for CloudFormation to invoke Lambda +- **Template Constraint** — locks `LambdaFunctionArn` to the deployed function ARN - **API Gateway** — HTTP API with POST /template endpoint (alternative invocation) -- **SSM Parameters** — GitHub API URL, org name, template repo, commit author settings -- **CloudWatch Log Group** — managed with configurable retention -- **Lambda Permission** — allows CloudFormation to invoke the function as a Custom Resource -- **Secrets Manager** — stores the GitHub token (optional creation via Terraform) +- **CloudWatch Log Group** — `/aws/lambda/eks-terragrunt-repo-gen-template-automation` -## Runtime Architecture +## Testing -``` -Service Catalog Product (CloudFormation Template) - ↓ - CloudFormation Stack Provisioning - ↓ - Custom Resource Invokes Lambda - ↓ - Lambda Creates Repository - ↓ - GitHub/GitLab Repository + Configuration PR -``` +### End-to-End Test Script (`scripts/test_workflow.py`) -## CloudFormation Event Structure - -The Lambda expects CloudFormation Custom Resource events in this format: - -```json -{ - "RequestType": "Create", - "ResponseURL": "pre-signed-url", - "StackId": "arn:aws:cloudformation:...", - "RequestId": "unique-id", - "LogicalResourceId": "MyRepository", - "ResourceType": "Custom::RepositoryCreator", - "ResourceProperties": { - "ServiceToken": "arn:aws:lambda:...:function:repo-generator", - "ProjectName": "my-new-repository", - "OwningTeam": "platform-team", - "Environment": "development", - "AwsRegion": "us-east-1" - } -} -``` +The test script validates the entire stack without using Service Catalog: + +```bash +# Generic (non-EKS) test — creates "workflow-test-" repo +python scripts/test_workflow.py -## CloudFormation Template Example - -See `cloudformation-template.yaml` for a complete Service Catalog product template: - -```yaml -Resources: - GitHubRepository: - Type: Custom::RepositoryCreator - Properties: - ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:repo-generator' - ProjectName: !Ref ProjectName - OwningTeam: !Ref OwningTeam - Environment: !Ref Environment - -Outputs: - RepositoryUrl: - Value: !GetAtt GitHubRepository.RepositoryUrl - PullRequestUrl: - Value: !GetAtt GitHubRepository.PullRequestUrl +# EKS deployment test — creates "eks-test-" repo with full HCL rendering +python scripts/test_workflow.py --eks + +# Custom repo name +python scripts/test_workflow.py my-test-repo +python scripts/test_workflow.py --eks my-eks-test ``` -## Required Parameters +The script runs 6 sequential checks: -### Core Parameters (Required) +| Step | What It Does | +|------|-------------| +| 1. AWS Credentials | Validates STS identity and account | +| 2. Lambda Env Vars | Reads Lambda configuration, checks required vars | +| 3. Secrets Manager | Retrieves GitHub token, validates format | +| 4. GitHub API | Tests authentication against GHE `/user` endpoint | +| 5. Lambda Invoke | Sends async CloudFormation Custom Resource event | +| 6. CloudWatch Tail | Tails logs for 120s, watches for SUCCESS/FAILED | -- `ProjectName` (string): Name of the repository to create -- `OwningTeam` (string): GitHub team that should have admin access (default: "tf-module-admins") +Output includes a Rich-formatted summary table and pass/fail panel with repo and PR URLs. -### Additional Parameters (Optional) +### GitHub Permissions Checker -All other parameters in `ResourceProperties` are collected and stored in the repository's `config.json` file. +Validates that the PAT has all required scopes before deployment: -**Note**: CloudFormation uses PascalCase for parameters. The Lambda automatically converts them to snake_case: -- `ProjectName` → `project_name` -- `OwningTeam` → `owning_team` -- `AwsRegion` → `aws_region` +```bash +python scripts/check_github_permissions.py +python scripts/check_github_permissions.py --token ghp_... --org MyOrg +``` + +Checks 11 API operations: `/user`, `/user/orgs`, `/orgs/{org}`, membership, repo listing, repo creation, template access, branch listing, git ref creation, PR creation, and team permissions. -## Lambda Workflow (Runtime) +### Direct Lambda Invocation + +```bash +aws lambda invoke \ + --function-name eks-terragrunt-repo-gen-template-automation \ + --payload file://events/cloudformation-create-event.json \ + --region us-gov-west-1 \ + response.json +``` -1. **User Provisions**: User provisions Service Catalog product -2. **CloudFormation Runs**: CloudFormation stack is created -3. **Lambda Invoked**: Custom Resource invokes Lambda with parameters -4. **Create Repository**: Lambda creates new repository in GitHub/GitLab -5. **Clone Template**: Copies contents from template repository -6. **Write Config**: Creates `config.json` with all parameters -7. **Create PR**: Opens pull request with the configuration -8. **Set Permissions**: Assigns team permissions (GitHub only) -9. **Return Success**: Lambda sends success response to CloudFormation -10. **Stack Complete**: CloudFormation stack completes with repository URLs in outputs +### CloudFormation Stack Test -## Configuration File Structure +```bash +aws cloudformation create-stack \ + --stack-name test-eks-repo \ + --template-body file://service-catalog/product-template.yaml \ + --parameters \ + ParameterKey=ProjectName,ParameterValue=test-eks-$(date +%s) \ + ParameterKey=AccountName,ParameterValue=csvd-dev-ew \ + ParameterKey=AWSAccountId,ParameterValue=229685449397 \ + ParameterKey=EnvironmentAbbr,ParameterValue=dev \ + ParameterKey=VpcName,ParameterValue=csvd-dev-vpc \ + ParameterKey=VpcDomainName,ParameterValue=dev.inf.csp1.census.gov \ + --region us-gov-west-1 +``` -The Lambda creates a `config.json` file in the repository with this structure: +## Runtime Architecture -```json -{ - "attrs": { - "aws_region": "us-east-1", - "environment": "development" - }, - "tags": {} -} +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ User → AWS Service Catalog UI → "Launch Product" │ +│ ↓ │ +│ CloudFormation creates stack │ +│ ↓ │ +│ Custom::GitHubRepository resource invokes Lambda │ +│ ↓ │ +│ Lambda (template_automation.app.lambda_handler) │ +│ ├─ Validates CloudFormation parameters (Pydantic) │ +│ ├─ Creates repository in GitHub (internal visibility) │ +│ ├─ Clones template-eks-cluster contents │ +│ ├─ Creates repo-init branch │ +│ ├─ Detects EKS deployment? ─┬─ YES: render 8 HCL files + config │ +│ │ └─ NO: write single config.json │ +│ ├─ Atomic commit via Git tree API │ +│ ├─ Opens PR: repo-init → main │ +│ ├─ Sets team permissions (admin) │ +│ └─ Returns SUCCESS + URLs to CloudFormation │ +│ ↓ │ +│ CloudFormation completes → Outputs: RepositoryUrl, PullRequestUrl │ +└──────────────────────────────────────────────────────────────────────┘ ``` -## Environment Variables +## Lambda Environment Variables -The Lambda function uses the following environment variables (configured via SSM Parameters and Terraform): +| Variable | Description | Example | +|----------|-------------|---------| +| `GITHUB_API` | GitHub Enterprise API URL | `https://github.e.it.census.gov` | +| `GITHUB_TOKEN_SECRET_NAME` | Secrets Manager path for PAT | `/eks-cluster-deployment/github_token` | +| `GITHUB_ORG_NAME` | GitHub organization | `SCT-Engineering` | +| `TEMPLATE_REPO_NAME` | Template repo to clone from | `template-eks-cluster` | +| `REPO_VISIBILITY` | Repo visibility (private/internal/public) | `internal` | +| `VERIFY_SSL` | SSL verification (`true`/`false`) | `false` (until CA cert baked in) | +| `PARAM_STORE_PREFIX` | SSM parameter path prefix | `/eks-terragrunt-repo-gen` | +| `TEMPLATE_CONFIG_FILE` | Config filename (optional) | `config.json` | -| Variable | Description | Source | -|----------|-------------|--------| -| `GITHUB_API` | GitHub API URL | SSM Parameter | -| `GITHUB_TOKEN_SECRET_NAME` | Secrets Manager secret name for GitHub token | Lambda env var | -| `GITHUB_ORG_NAME` | GitHub organization name | SSM Parameter | -| `TEMPLATE_REPO_NAME` | Template repository to clone from | SSM Parameter | -| `TEMPLATE_CONFIG_FILE` | Config filename (default: `config.json`) | SSM Parameter | -| `VERIFY_SSL` | Enable/disable SSL verification | Lambda env var | -| `PARAM_STORE_PREFIX` | SSM parameter path prefix | Lambda env var | +## EKS Deployment Detection + +The Lambda automatically detects EKS deployments when the incoming parameters include all of: +- `cluster_name` +- `account_name` +- `aws_account_id` +- `vpc_name` +- `vpc_domain_name` + +When detected, it uses `eks_config.py` to build a fully-hydrated `EKSDeploymentConfig` Pydantic model and renders all 8 Jinja2 templates into HCL files. ## Development -### Running Tests +### Install Dependencies ```bash -# Install dependencies pip install -r requirements.txt pip install -e . +``` + +### Run Tests -# Run unit tests -python -m pytest tests/test_app.py tests/test_github_client.py -v +```bash +# Unit tests +make test-unit + +# Integration tests (requires GitHub token) +make test-integration -# Run integration tests (requires GitHub token) -python -m pytest tests/integration/ -v +# All tests +make test ``` -### Related Repositories +### Clean Up Test Repositories + +```bash +python scripts/cleanup_test_repos.py +# or +make clean-test-repos +``` + +## Monitoring + +```bash +# Tail Lambda logs +aws logs tail /aws/lambda/eks-terragrunt-repo-gen-template-automation \ + --follow --region us-gov-west-1 + +# Filter for errors +aws logs filter-log-events \ + --log-group-name /aws/lambda/eks-terragrunt-repo-gen-template-automation \ + --filter-pattern "ERROR" --region us-gov-west-1 +``` -- [packer-pipeline](https://github.e.it.census.gov/SCT-Engineering/packer-pipeline) — Build tool for container images via CodeBuild -- [terraform-aws-template-automation](https://github.com/HappyPathway/terraform-aws-template-automation) — Terraform module for Lambda deployment -- [template-automation-lambda](https://github.e.it.census.gov/SCT-Engineering/template-automation-lambda) — Related Lambda implementation +## Related Repositories + +| Repository | Purpose | +|-----------|---------| +| [packer-pipeline](https://github.e.it.census.gov/SCT-Engineering/packer-pipeline) | Build tool — packages code, runs CodeBuild, pushes to ECR | +| [terraform-aws-template-automation](https://github.e.it.census.gov/SCT-Engineering/terraform-aws-template-automation) | Terraform module — deploys Lambda, IAM, API Gateway, SSM | +| [template-eks-cluster](https://github.e.it.census.gov/SCT-Engineering/template-eks-cluster) | Template repo — cloned as starting point for new EKS repos | +| [terraform-eks-deployment](https://github.e.it.census.gov/SCT-Engineering/terraform-eks-deployment) | Reference — Terragrunt structure this Lambda replicates | + +## Documentation Index + +| Document | Description | +|----------|-------------| +| [DEPLOYMENT.md](DEPLOYMENT.md) | Step-by-step deployment guide | +| [CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md](CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md) | How Custom Resources work, Service Catalog setup, troubleshooting | +| [CLOUDFORMATION_CUSTOM_RESOURCE_MIGRATION.md](CLOUDFORMATION_CUSTOM_RESOURCE_MIGRATION.md) | Migration notes from EventBridge to Custom Resources | +| [PACKER_PIPELINE_EXPLANATION.md](PACKER_PIPELINE_EXPLANATION.md) | Resolving the `terraform_version` variable issue | +| [PACKER_UPDATES.md](PACKER_UPDATES.md) | Packer/container build configuration changes | +| [design-docs/README.md](design-docs/README.md) | System architecture and implementation phases | +| [design-docs/CUSTOM_TEMPLATES.MD](design-docs/CUSTOM_TEMPLATES.MD) | Subdirectory and multi-template patterns | +| [docs/SERVICE_CATALOG_RESOLUTION.md](docs/SERVICE_CATALOG_RESOLUTION.md) | S3 tag-based access fix for Service Catalog | diff --git a/cloudformation-template.yaml b/cloudformation-template.yaml index 41ffe949..e0fd95ee 100644 --- a/cloudformation-template.yaml +++ b/cloudformation-template.yaml @@ -58,7 +58,7 @@ Parameters: LambdaFunctionArn: Type: String Description: ARN of the Lambda function that creates repositories - Default: arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation + Default: arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:eks-terragrunt-repo-gen-template-automation Resources: # Custom Resource that invokes the Lambda function diff --git a/config_packer.hcl b/config_packer.hcl index 39d84354..9d2e12c2 100644 --- a/config_packer.hcl +++ b/config_packer.hcl @@ -1,11 +1,12 @@ -// config_packer.hcl - Packer Pipeline Configuration for Service Catalog Repository Generator Lambda +// config_packer.hcl - Packer Pipeline Configuration for EKS Terragrunt Repository Generator Lambda +// Builds the Lambda container that renders Terragrunt HCL files into new GitHub repos. packer_pipeline { // Required parameters packer_template_file = "packer.pkr.hcl" // Relative path within the repo to the Packer template s3_bucket = "csvd-template-automation-builds" // S3 bucket for artifacts assets_bucket = "image-pipeline-assets-dev" // S3 bucket containing tool assets - codebuild_project_name = "service-catalog-repo-generator-builder" // Name for the CodeBuild project + codebuild_project_name = "eks-terragrunt-repo-generator-builder" // Name for the CodeBuild project // Tools configuration tools = [ @@ -30,7 +31,7 @@ packer_pipeline { gov_cloud = true // Explicitly set GovCloud partition // Optional parameters with defaults - s3_key_prefix = "packer-builds/service-catalog-repo-generator" // Prefix for S3 keys + s3_key_prefix = "packer-builds/eks-terragrunt-repo-generator" // Prefix for S3 keys compute_type = "BUILD_GENERAL1_MEDIUM" // CodeBuild compute type image = "aws/codebuild/amazonlinux2-x86_64-standard:3.0" // CodeBuild image buildspec_template = "buildspec.yml.j2" // Buildspec template file @@ -57,8 +58,8 @@ packer_pipeline { // Environment variables for the CodeBuild environment environment_variables = { - REPOSITORY_NAME = "service-catalog-repo-generator-lambda" - ECR_REPOSITORY = "service-catalog-repo-generator/lambda" + REPOSITORY_NAME = "eks-terragrunt-repo-generator-lambda" + ECR_REPOSITORY = "eks-terragrunt-repo-generator/lambda" AWS_ACCOUNT_ID = "229685449397" IMAGE_TAG = "latest" HTTP_PROXY = "http://proxy.tco.census.gov:3128" @@ -68,7 +69,7 @@ packer_pipeline { } // ECR Image Cloning Configuration - ecr_registry_name = "service-catalog-repo-generator" // ECR registry prefix for cloned images + ecr_registry_name = "eks-terragrunt-repo-generator" // ECR registry prefix for cloned images ecr_clone_images = [ { diff --git a/deploy/.terraform_commits b/deploy/.terraform_commits index 41fe3b77..b1d2acad 100644 --- a/deploy/.terraform_commits +++ b/deploy/.terraform_commits @@ -76,5 +76,11 @@ "commit_message": "Update README with build/deploy workflow, fix Makefile paths\n\n- Rewrite README to document the 3-step build/deploy workflow:\n 1. Terraform creates ECR repository (root main.tf)\n 2. Packer Pipeline builds container image via CodeBuild\n 3. Terraform deploys Lambda function (deploy/ directory)\n- Add repository structure overview\n- Add environment variables reference table\n- Add development/testing instructions\n- Add related repositories links\n- Fix Makefile: update paths from eks_automation to template_automation\n- Fix deploy/main.tf: remove duplicate CloudFormation lambda_permission\n (already created by terraform-aws-template-automation module)\n- Add cloudwatch_log_group and api_endpoint outputs to deploy", "author": "Your Name", "timestamp": "2026-02-09T18:03:25.014617" + }, + { + "commit_hash": "20b8d7f04bb9aceea36e71e8b2d630e22dd54c7a", + "commit_message": "pushing latest code", + "author": "Your Name", + "timestamp": "2026-02-11T17:09:42.508401" } ] \ No newline at end of file diff --git a/deploy/main.tf b/deploy/main.tf index 68dff9f0..016844c3 100644 --- a/deploy/main.tf +++ b/deploy/main.tf @@ -1,5 +1,7 @@ -# Deployment configuration for Service Catalog Repository Generator Lambda +# Deployment configuration for EKS Terragrunt Repository Generator Lambda # This uses the terraform-aws-template-automation module to deploy the Lambda function +# that creates GitHub repos pre-populated with rendered Terragrunt/HCL configuration +# for EKS cluster deployments. terraform { required_version = ">= 1.0" @@ -21,10 +23,10 @@ data "aws_caller_identity" "current" {} data "aws_region" "current" {} # Deploy the Lambda function and supporting infrastructure -module "service_catalog_repo_generator" { +module "eks_terragrunt_repo_generator" { source = "../../terraform-aws-template-automation" - name_prefix = "service-catalog-repo-gen" + name_prefix = "eks-terragrunt-repo-gen" # GitHub configuration github_api_url = var.github_api_url @@ -38,11 +40,11 @@ module "service_catalog_repo_generator" { # Lambda configuration lambda_config = { - image_uri = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/service-catalog-repo-generator/lambda:${var.image_tag}" + image_uri = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${data.aws_region.current.name}.amazonaws.com/eks-terragrunt-repo-generator/lambda:${var.image_tag}" memory_size = 512 timeout = 300 - # VPC configuration (if needed for GitHub Enterprise access) + # VPC configuration (required for GitHub Enterprise access) vpc_config = var.enable_vpc ? { subnet_ids = var.subnet_ids security_group_ids = var.security_group_ids @@ -63,41 +65,48 @@ module "service_catalog_repo_generator" { # Outputs output "lambda_function_arn" { description = "ARN of the deployed Lambda function - use this as ServiceToken in CloudFormation" - value = module.service_catalog_repo_generator.lambda_function_arn + value = module.eks_terragrunt_repo_generator.lambda_function_arn } output "lambda_function_name" { description = "Name of the deployed Lambda function" - value = module.service_catalog_repo_generator.lambda_function_name + value = module.eks_terragrunt_repo_generator.lambda_function_name } output "cloudwatch_log_group" { description = "CloudWatch Log Group for Lambda function logs" - value = module.service_catalog_repo_generator.cloudwatch_log_group_name + value = module.eks_terragrunt_repo_generator.cloudwatch_log_group_name } output "api_endpoint" { description = "API Gateway endpoint (alternative invocation method)" - value = module.service_catalog_repo_generator.api_endpoint + value = module.eks_terragrunt_repo_generator.api_endpoint } output "cloudformation_template_example" { - description = "Example CloudFormation Custom Resource definition" + description = "Example CloudFormation Custom Resource definition for EKS cluster repo" value = <<-EOT Resources: - MyRepository: - Type: Custom::RepositoryCreator + MyEKSClusterRepo: + Type: Custom::GitHubRepository Properties: - ServiceToken: ${module.service_catalog_repo_generator.lambda_function_arn} - ProjectName: my-new-repo - OwningTeam: platform-team - Environment: development + ServiceToken: ${module.eks_terragrunt_repo_generator.lambda_function_arn} + project_name: my-eks-cluster + owning_team: platform-team + cluster_name: my-eks-cluster + environment: dev + aws_region: us-gov-west-1 + account_name: csvd-dev-ew + aws_account_id: "123456789012" + environment_abbr: dev + vpc_name: csvd-dev-ew-vpc-01 + vpc_domain_name: dev.inf.csp1.census.gov Outputs: RepositoryUrl: - Value: !GetAtt MyRepository.RepositoryUrl + Value: !GetAtt MyEKSClusterRepo.repository_url PullRequestUrl: - Value: !GetAtt MyRepository.PullRequestUrl + Value: !GetAtt MyEKSClusterRepo.pull_request_url EOT } diff --git a/deploy/service_catalog.tf b/deploy/service_catalog.tf index 1b0f015e..ac91c7da 100644 --- a/deploy/service_catalog.tf +++ b/deploy/service_catalog.tf @@ -7,9 +7,9 @@ data "aws_partition" "current" {} locals { create_sc = var.create_service_catalog product_template = "${path.module}/../service-catalog/product-template.yaml" - lambda_arn = module.service_catalog_repo_generator.lambda_function_arn + lambda_arn = module.eks_terragrunt_repo_generator.lambda_function_arn artifacts_bucket = var.service_catalog_config.artifacts_bucket_name - product_s3_key = "github-repo-creator/v${var.service_catalog_config.product_version}/product-template.yaml" + product_s3_key = "eks-terragrunt-repo-creator/v${var.service_catalog_config.product_version}/product-template.yaml" template_url = "https://${local.artifacts_bucket}.s3.${data.aws_region.current.name}.amazonaws.com/${local.product_s3_key}" } @@ -23,7 +23,9 @@ resource "aws_s3_object" "product_template" { source = local.product_template etag = filemd5(local.product_template) - tags = var.tags + tags = merge(var.tags, { + "servicecatalog:provisioning" = "true" + }) } # ----------------------------------------------------------------------------- @@ -45,9 +47,9 @@ resource "aws_servicecatalog_portfolio" "this" { resource "aws_servicecatalog_product" "github_repository" { count = local.create_sc ? 1 : 0 - name = "${var.service_catalog_config.portfolio_name_prefix}-github-repo-creator" + name = "${var.service_catalog_config.portfolio_name_prefix}-eks-repo-creator" owner = var.service_catalog_config.provider_name - description = "Create a GitHub repository from an approved template with standard configuration, branch protection, and team access." + description = "Create an EKS cluster GitHub repository from a Terragrunt template with fully rendered HCL configuration, branch protection, and team access." type = "CLOUD_FORMATION_TEMPLATE" provisioning_artifact_parameters { @@ -145,7 +147,12 @@ resource "aws_iam_role_policy" "service_catalog_launch" { Action = [ "s3:GetObject" ] - Resource = "arn:${data.aws_partition.current.partition}:s3:::${local.artifacts_bucket}/*" + Resource = "*" + Condition = { + "StringEquals" = { + "s3:ExistingObjectTag/servicecatalog:provisioning" = ["true"] + } + } }, { Sid = "S3ListBucket" diff --git a/deploy/terraform.tfstate b/deploy/terraform.tfstate index 6802cb3c..442d8754 100644 --- a/deploy/terraform.tfstate +++ b/deploy/terraform.tfstate @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.9.1", - "serial": 90, + "serial": 95, "lineage": "637f189b-ce2c-766c-35d1-8b43eb7ae216", "outputs": { "api_endpoint": { @@ -150,7 +150,7 @@ "id": "github-automation-sc-launch-role:invoke-lambda-and-cfn", "name": "invoke-lambda-and-cfn", "name_prefix": "", - "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"lambda:InvokeFunction\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation\",\"Sid\":\"InvokeLambda\"},{\"Action\":[\"cloudformation:CreateStack\",\"cloudformation:DeleteStack\",\"cloudformation:DescribeStacks\",\"cloudformation:DescribeStackEvents\",\"cloudformation:GetTemplate\",\"cloudformation:GetTemplateSummary\",\"cloudformation:ValidateTemplate\",\"cloudformation:UpdateStack\",\"cloudformation:SetStackPolicy\"],\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"CloudFormationOperations\"},{\"Action\":[\"s3:GetObject\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:s3:::servicecatalog-product-artifacts-20250904021619588100000003/*\",\"Sid\":\"S3ReadTemplate\"},{\"Action\":[\"s3:ListBucket\",\"s3:GetBucketLocation\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:s3:::servicecatalog-product-artifacts-20250904021619588100000003\",\"Sid\":\"S3ListBucket\"}]}", + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"lambda:InvokeFunction\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation\",\"Sid\":\"InvokeLambda\"},{\"Action\":[\"cloudformation:CreateStack\",\"cloudformation:DeleteStack\",\"cloudformation:DescribeStacks\",\"cloudformation:DescribeStackEvents\",\"cloudformation:GetTemplate\",\"cloudformation:GetTemplateSummary\",\"cloudformation:ValidateTemplate\",\"cloudformation:UpdateStack\",\"cloudformation:SetStackPolicy\"],\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"CloudFormationOperations\"},{\"Action\":[\"s3:GetObject\"],\"Condition\":{\"StringEquals\":{\"s3:ExistingObjectTag/servicecatalog:provisioning\":[\"true\"]}},\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"S3ReadTemplate\"},{\"Action\":[\"s3:ListBucket\",\"s3:GetBucketLocation\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:s3:::servicecatalog-product-artifacts-20250904021619588100000003\",\"Sid\":\"S3ListBucket\"}]}", "role": "github-automation-sc-launch-role" }, "sensitive_attributes": [], @@ -213,12 +213,14 @@ "tags": { "Environment": "production", "ManagedBy": "Terraform", - "Purpose": "ServiceCatalogRepoGenerator" + "Purpose": "ServiceCatalogRepoGenerator", + "servicecatalog:provisioning": "true" }, "tags_all": { "Environment": "production", "ManagedBy": "Terraform", - "Purpose": "ServiceCatalogRepoGenerator" + "Purpose": "ServiceCatalogRepoGenerator", + "servicecatalog:provisioning": "true" }, "version_id": "", "website_redirect": "" @@ -240,7 +242,7 @@ "attributes": { "accept_language": "en", "description": "Launch constraint - uses a dedicated role to invoke the Lambda function", - "id": "cons-rewnawp5qi4dk", + "id": "cons-ufoejammwoed2", "owner": "229685449397", "parameters": "{\"RoleArn\":\"arn:aws-us-gov:iam::229685449397:role/github-automation-sc-launch-role\"}", "portfolio_id": "port-uchiqj7m3d57k", @@ -272,7 +274,7 @@ "attributes": { "accept_language": "en", "description": "Template constraint - locks the Lambda ARN to the deployed function", - "id": "cons-aburaudytn5f4", + "id": "cons-yg6qot2tchwy2", "owner": "229685449397", "parameters": "{\"Rules\":{\"LockLambdaArn\":{\"Assertions\":[{\"Assert\":{\"Fn::Equals\":[{\"Ref\":\"LambdaFunctionArn\"},\"arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation\"]},\"AssertDescription\":\"The Lambda function ARN cannot be changed\"}]}}}", "portfolio_id": "port-uchiqj7m3d57k", @@ -1172,13 +1174,13 @@ [ { "type": "get_attr", - "value": "value" + "value": "value_wo" } ], [ { "type": "get_attr", - "value": "value_wo" + "value": "value" } ] ], @@ -1266,13 +1268,13 @@ [ { "type": "get_attr", - "value": "value_wo" + "value": "value" } ], [ { "type": "get_attr", - "value": "value" + "value": "value_wo" } ] ], @@ -1313,13 +1315,13 @@ [ { "type": "get_attr", - "value": "value_wo" + "value": "value" } ], [ { "type": "get_attr", - "value": "value" + "value": "value_wo" } ] ], @@ -1407,13 +1409,13 @@ [ { "type": "get_attr", - "value": "value_wo" + "value": "value" } ], [ { "type": "get_attr", - "value": "value" + "value": "value_wo" } ] ], diff --git a/deploy/terraform.tfstate.backup b/deploy/terraform.tfstate.backup index 9261b513..6802cb3c 100644 --- a/deploy/terraform.tfstate.backup +++ b/deploy/terraform.tfstate.backup @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.9.1", - "serial": 80, + "serial": 90, "lineage": "637f189b-ce2c-766c-35d1-8b43eb7ae216", "outputs": { "api_endpoint": { @@ -29,11 +29,11 @@ "type": "string" }, "service_catalog_product_id": { - "value": "prod-t4s4ot42bg3au", + "value": "prod-w3uvfaxmeblxe", "type": "string" }, "service_catalog_provisioning_url": { - "value": "https://console.amazonaws-us-gov.com/servicecatalog/home?region=us-gov-west-1#/products/prod-t4s4ot42bg3au", + "value": "https://console.amazonaws-us-gov.com/servicecatalog/home?region=us-gov-west-1#/products/prod-w3uvfaxmeblxe", "type": "string" } }, @@ -111,7 +111,7 @@ "inline_policy": [ { "name": "invoke-lambda-and-cfn", - "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"lambda:InvokeFunction\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation\",\"Sid\":\"InvokeLambda\"},{\"Action\":[\"cloudformation:CreateStack\",\"cloudformation:DeleteStack\",\"cloudformation:DescribeStacks\",\"cloudformation:DescribeStackEvents\",\"cloudformation:GetTemplate\",\"cloudformation:GetTemplateSummary\",\"cloudformation:ValidateTemplate\",\"cloudformation:UpdateStack\",\"cloudformation:SetStackPolicy\"],\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"CloudFormationOperations\"},{\"Action\":[\"s3:GetObject\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:s3:::github-automation-templates-229685449397/*\",\"Sid\":\"S3ReadTemplate\"},{\"Action\":[\"s3:ListBucket\",\"s3:GetBucketLocation\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:s3:::github-automation-templates-229685449397\",\"Sid\":\"S3ListBucket\"}]}" + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"lambda:InvokeFunction\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation\",\"Sid\":\"InvokeLambda\"},{\"Action\":[\"cloudformation:CreateStack\",\"cloudformation:DeleteStack\",\"cloudformation:DescribeStacks\",\"cloudformation:DescribeStackEvents\",\"cloudformation:GetTemplate\",\"cloudformation:GetTemplateSummary\",\"cloudformation:ValidateTemplate\",\"cloudformation:UpdateStack\",\"cloudformation:SetStackPolicy\"],\"Effect\":\"Allow\",\"Resource\":\"*\",\"Sid\":\"CloudFormationOperations\"},{\"Action\":[\"s3:GetObject\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:s3:::servicecatalog-product-artifacts-20250904021619588100000003/*\",\"Sid\":\"S3ReadTemplate\"},{\"Action\":[\"s3:ListBucket\",\"s3:GetBucketLocation\"],\"Effect\":\"Allow\",\"Resource\":\"arn:aws-us-gov:s3:::servicecatalog-product-artifacts-20250904021619588100000003\",\"Sid\":\"S3ListBucket\"}]}" } ], "managed_policy_arns": [], @@ -195,13 +195,13 @@ "content_disposition": "", "content_encoding": "", "content_language": "", - "content_type": "application/octet-stream", + "content_type": "binary/octet-stream", "etag": "fe84992f754d4776f5b3242b952a8d84", "force_destroy": false, "id": "github-repo-creator/v1.0/product-template.yaml", "key": "github-repo-creator/v1.0/product-template.yaml", "kms_key_id": null, - "metadata": null, + "metadata": {}, "object_lock_legal_hold_status": "", "object_lock_mode": "", "object_lock_retain_until_date": "", @@ -240,11 +240,11 @@ "attributes": { "accept_language": "en", "description": "Launch constraint - uses a dedicated role to invoke the Lambda function", - "id": "cons-khntuuy33hpco", + "id": "cons-rewnawp5qi4dk", "owner": "229685449397", "parameters": "{\"RoleArn\":\"arn:aws-us-gov:iam::229685449397:role/github-automation-sc-launch-role\"}", "portfolio_id": "port-uchiqj7m3d57k", - "product_id": "prod-t4s4ot42bg3au", + "product_id": "prod-w3uvfaxmeblxe", "status": "AVAILABLE", "timeouts": null, "type": "LAUNCH" @@ -272,11 +272,11 @@ "attributes": { "accept_language": "en", "description": "Template constraint - locks the Lambda ARN to the deployed function", - "id": "cons-5rsgzzjtwkej2", + "id": "cons-aburaudytn5f4", "owner": "229685449397", "parameters": "{\"Rules\":{\"LockLambdaArn\":{\"Assertions\":[{\"Assert\":{\"Fn::Equals\":[{\"Ref\":\"LambdaFunctionArn\"},\"arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation\"]},\"AssertDescription\":\"The Lambda function ARN cannot be changed\"}]}}}", "portfolio_id": "port-uchiqj7m3d57k", - "product_id": "prod-t4s4ot42bg3au", + "product_id": "prod-w3uvfaxmeblxe", "status": "AVAILABLE", "timeouts": null, "type": "TEMPLATE" @@ -366,12 +366,12 @@ "schema_version": 0, "attributes": { "accept_language": "en", - "arn": "arn:aws-us-gov:catalog:us-gov-west-1:229685449397:product/prod-t4s4ot42bg3au", - "created_time": "2026-02-09T22:57:41Z", + "arn": "arn:aws-us-gov:catalog:us-gov-west-1:229685449397:product/prod-w3uvfaxmeblxe", + "created_time": "2026-02-09T23:03:16Z", "description": "Create a GitHub repository from an approved template with standard configuration, branch protection, and team access.", "distributor": "", "has_default_path": false, - "id": "prod-t4s4ot42bg3au", + "id": "prod-w3uvfaxmeblxe", "name": "github-automation-github-repo-creator", "owner": "Platform Engineering", "provisioning_artifact_parameters": [ @@ -380,7 +380,7 @@ "disable_template_validation": false, "name": "v1.0", "template_physical_id": "", - "template_url": "https://s3.us-gov-west-1.amazonaws.com/servicecatalog-product-artifacts-20250904021619588100000003/github-repo-creator/v1.0/product-template.yaml", + "template_url": "https://servicecatalog-product-artifacts-20250904021619588100000003.s3.us-gov-west-1.amazonaws.com/github-repo-creator/v1.0/product-template.yaml", "type": "CLOUD_FORMATION_TEMPLATE" } ], @@ -420,9 +420,9 @@ "schema_version": 0, "attributes": { "accept_language": "en", - "id": "en:port-uchiqj7m3d57k:prod-t4s4ot42bg3au", + "id": "en:port-uchiqj7m3d57k:prod-w3uvfaxmeblxe", "portfolio_id": "port-uchiqj7m3d57k", - "product_id": "prod-t4s4ot42bg3au", + "product_id": "prod-w3uvfaxmeblxe", "source_portfolio_id": "", "timeouts": null }, @@ -1172,13 +1172,13 @@ [ { "type": "get_attr", - "value": "value_wo" + "value": "value" } ], [ { "type": "get_attr", - "value": "value" + "value": "value_wo" } ] ], @@ -1407,13 +1407,13 @@ [ { "type": "get_attr", - "value": "value" + "value": "value_wo" } ], [ { "type": "get_attr", - "value": "value_wo" + "value": "value" } ] ], diff --git a/deploy/terraform.tfvars b/deploy/terraform.tfvars index db49b2f5..fd0b09b2 100644 --- a/deploy/terraform.tfvars +++ b/deploy/terraform.tfvars @@ -1,31 +1,41 @@ -# Example terraform.tfvars file for deploying the Service Catalog Repository Generator +# terraform.tfvars — EKS Terragrunt Repository Generator +# Deploys Lambda + Service Catalog product for creating EKS cluster repos +# with fully-rendered Terragrunt/HCL configuration from Jinja2 templates. aws_region = "us-gov-west-1" -github_api_url = "https://github.e.it.census.gov" # Your GitHub Enterprise URL (code adds /api/v3 automatically) -github_org_name = "SCT-Engineering" # Your GitHub organization name +# ── GitHub Enterprise Configuration ────────────────────────────────────── +github_api_url = "https://github.e.it.census.gov" # GHE URL (code adds /api/v3 automatically) +github_org_name = "SCT-Engineering" template_repo_name = "template-eks-cluster" -# GHE enterprise policy blocks 'private' repo creation for org members; use 'internal' -repo_visibility = "internal" -create_service_catalog = true +repo_visibility = "internal" # GHE enterprise policy blocks 'private'; use 'internal' github_token_secret_name = "/eks-cluster-deployment/github_token" +# ── Service Catalog ────────────────────────────────────────────────────── +create_service_catalog = true + service_catalog_config = { + portfolio_name_prefix = "eks-terragrunt" + portfolio_description = "Self-service EKS cluster repository creation with Terragrunt configuration" + provider_name = "Platform Engineering" + product_version = "2.0" artifacts_bucket_name = "servicecatalog-product-artifacts-20250904021619588100000003" principal_arns = [ "arn:aws-us-gov:iam::229685449397:role/aws-reserved/sso.amazonaws.com/us-gov-east-1/AWSReservedSSO_inf-admin-t2_4e0c6446aecbe4a0" ] } -image_tag = "latest" # Or specific version like "1.0.0" +# ── Lambda Container Image ────────────────────────────────────────────── +image_tag = "latest" -# If you need VPC access for GitHub Enterprise +# ── VPC Configuration (required for GHE access) ───────────────────────── enable_vpc = true subnet_ids = ["subnet-0b1992a84536c581b"] security_group_ids = ["sg-0641c697588b9aa6b"] +# ── Tags ───────────────────────────────────────────────────────────────── tags = { ManagedBy = "Terraform" - Purpose = "ServiceCatalogRepoGenerator" + Purpose = "EKSTerragruntRepoGenerator" Environment = "production" } diff --git a/deploy/variables.tf b/deploy/variables.tf index 7212e75d..ca39c64f 100644 --- a/deploy/variables.tf +++ b/deploy/variables.tf @@ -60,7 +60,7 @@ variable "tags" { type = map(string) default = { ManagedBy = "Terraform" - Purpose = "ServiceCatalogRepoGenerator" + Purpose = "EKSTerragruntRepoGenerator" } } @@ -69,7 +69,7 @@ variable "tags" { # ----------------------------------------------------------------------------- variable "create_service_catalog" { - description = "Whether to create a Service Catalog portfolio and product for this Lambda" + description = "Whether to create a Service Catalog portfolio and product for self-service EKS repo provisioning" type = bool default = false } @@ -78,10 +78,10 @@ variable "create_service_catalog" { variable "service_catalog_config" { description = "Configuration for the Service Catalog portfolio and product" type = object({ - portfolio_name_prefix = optional(string, "github-automation") - portfolio_description = optional(string, "Self-service GitHub repository creation from approved templates") + portfolio_name_prefix = optional(string, "eks-terragrunt") + portfolio_description = optional(string, "Self-service EKS cluster repository creation with Terragrunt configuration") provider_name = optional(string, "Platform Engineering") - product_version = optional(string, "1.0") + product_version = optional(string, "2.0") principal_arns = optional(list(string), []) artifacts_bucket_name = string }) diff --git a/design-docs/README.md b/design-docs/README.md index f1015161..1a615311 100644 --- a/design-docs/README.md +++ b/design-docs/README.md @@ -1,144 +1,113 @@ -# Template Automation System Implementation Plan +# Template Automation System — Architecture & Implementation Status ## System Architecture -The Template Automation System is designed to be a generic, template-agnostic infrastructure that can automate the creation and configuration of any type of repository from a template. The system consists of two core components and can work with any number of template repositories. +The Template Automation System automates the creation and configuration of GitHub repositories from templates via AWS Service Catalog. It consists of three core components: ### Core Components #### terraform-aws-template-automation -This is the foundational Terraform module that deploys the automation infrastructure: -- Deploys the Lambda function and required AWS resources (IAM roles, etc.) -- Configures the Lambda as a CloudFormation Custom Resource integration -- Manages any required SSM parameters or Secrets -- Provides a reusable module that can be included in any AWS environment -- Template-agnostic - works with any type of repository template - -#### template-automation-lambda -This is the engine of the automation system: -- Implements the core repository templating logic in template_automation/app.py -- Packaged as a Docker image for Lambda deployment -- Handles CloudFormation Custom Resource events (Create, Update, Delete) -- Handles repository creation, branch management, and PR automation -- Template-agnostic - can work with any properly structured template repository - -### Template Repositories - -#### template-eks-cluster (Example) -This is an example template repository that demonstrates how to structure a template for use with the automation system: -- Shows the pattern for creating EKS clusters -- Serves as a reference implementation -- Demonstrates best practices for template structure -- One of many possible templates that could be used with the system - -### Build Infrastructure Requirements -The Terraform configuration in this repository is specifically for building the Lambda container image in ECR. Due to tooling restrictions and access requirements, the build process must be executed in GitHub.com rather than in the target organization's environment. This means: - -- The container image build pipeline runs in GitHub.com -- Terraform in this repo manages only build-related resources (ECR repository, build IAM roles) -- The build process cannot access internal tools or resources of the target organization -- The resulting container image is then referenced by the terraform-aws-template-automation module for actual deployment - -## Overview -This document outlines the implementation plan for the Template Automation System, using an EKS cluster template as our first case study. While we'll be working with the `template-eks-cluster` repository to validate and demonstrate the system's capabilities, the core automation components (`template-automation-lambda` and `terraform-aws-template-automation`) are designed to work with any properly structured template repository. - -The EKS cluster template serves as an excellent first example because it: -- Demonstrates complex configuration processing requirements -- Shows how templates can define their own workflow automation -- Provides a real-world validation of the system's flexibility -- Establishes patterns that other templates can follow - -Most of the core automation work will take place in `template_automation/app.py`, while the EKS-specific template logic resides in the `template-eks-cluster` repository. This separation ensures that our automation system remains template-agnostic while allowing templates to define their own specialized behavior. - -## Implementation Phases - -### Phase 1: Lambda Function Core Updates -Updates to the Lambda function to establish template-agnostic repository management: - -- **Branch Management** - - Create new initialization branch instead of pushing directly to main - - Implement flexible branch creation in GitHub client (template_automation/app.py) - - Add robust error handling for branch operations - - Support template-specific branch naming (e.g., "init-cluster" for EKS templates) - -- **Pull Request Automation** - - Add automatic PR creation after pushing changes - - Implement configurable PR creation logic in GitHub client - - Support template-specific PR templates and descriptions - - Allow templates to define their PR strategies - -### Phase 2: Template Processing Framework -Enhance the framework for processing template repositories, using EKS template as reference: - -- **Configuration Processing** - - Create flexible configuration processing system - - Support multiple configuration formats (JSON, HCL, YAML) - - Allow templates to define custom processing logic - - Example: Implement config.js to HCL conversion via Ansible for EKS template - -- **GitHub Actions Framework** - - Create template-agnostic workflow framework - - Allow templates to define custom GitHub Actions - - Support environment-specific configurations - - Example: Implement EKS template's generate_hcl_files.yml playbook - -- **Runner Configuration** - - Implement account-specific runner selection - - Support lab environment runners - - Configure runners based on AWS account IDs - - Enable template-specific validation steps - -### Phase 3: Testing Implementation -Establish comprehensive testing framework for both core system and templates: - -- **Lab Environment Setup** - - Configure workflow for lab AWS account - - Set up isolated testing environment - - Create test configurations for various template types - - Example: Set up EKS cluster test configurations - -- **Core System Testing** - - Test template-agnostic functionality - - Validate GitHub integration components - - Test configuration processing framework - - Verify error handling and recovery - -- **End-to-End Testing** - - Implement full workflow testing - - Create demonstration environment - - Add integration tests for GitHub operations - - Test template-specific validations - - Example: Validate EKS cluster creation workflow - -### Phase 4: Documentation and Interface -Establish documentation and support infrastructure: - -- **Core System Documentation** - - Document Lambda invocation process - - Template structure requirements - - Configuration schema documentation - - Template processing hooks - -- **Template Development Guide** - - Template structure guidelines - - Best practices for template design - - Example implementations (using EKS template) - - Template testing guidelines - -- **Future Considerations** - - Additional template types beyond EKS - - Enhanced template processing capabilities - - Integration with other systems (e.g., CRF) - - Template marketplace concept - -## Success Criteria -- Core automation system successfully processes any valid template -- Templates can define their own processing logic and validation -- Comprehensive testing framework validates both system and templates -- Clear documentation helps users create new templates -- System demonstrates flexibility with multiple template types - -## Dependencies -- GitHub API access and permissions -- AWS account access for testing -- Runner configurations for different environments +Terraform module that deploys the automation infrastructure: +- Lambda function, IAM roles, API Gateway, SSM parameters +- Service Catalog portfolio, product, launch constraints +- CloudFormation permission for Custom Resource invocation +- Template-agnostic — works with any repository template + +#### lambda-template-repo-generator (this repo) +The engine of the automation system: +- `template_automation/app.py` — Lambda handler for CloudFormation Custom Resource events +- `template_automation/eks_config.py` — EKS-specific Pydantic models + Jinja2 renderer +- `template_automation/github_provider.py` — GitHub API provider (Git tree API for atomic commits) +- `template_automation/templates/eks/` — 8 Jinja2 templates for Terragrunt HCL files +- `service-catalog/product-template.yaml` — CloudFormation product template with EKS parameters +- Packaged as a Docker image, built via `packer-pipeline`, deployed to ECR + +#### template-eks-cluster +Example template repository that serves as the starting point: +- Cloned into every new EKS repo created by the Lambda +- Contains standard Terragrunt directory structure +- Lambda renders configuration files on top of this template + +### Build Infrastructure +- Container builds run via CodeBuild (triggered by `packer-pipeline` CLI) +- ECR repository: `eks-terragrunt-repo-generator/lambda` +- CodeBuild project: `eks-terragrunt-repo-generator-builder` + +## Implementation Status + +### Phase 1: Lambda Function Core ✅ COMPLETE +- [x] CloudFormation Custom Resource handler (Create, Update, Delete) +- [x] Branch management — creates `repo-init` branch, falls back to `main` +- [x] Pull request automation — PR from `repo-init` → `main` +- [x] Error handling with CloudFormation response protocol +- [x] PascalCase → snake_case parameter normalization (with acronym handling) + +### Phase 2: Template Processing Framework ✅ COMPLETE +- [x] EKS deployment detection (auto-detects from parameter set) +- [x] Pydantic v1 models for EKS config (`EKSDeploymentConfig`, `ClusterConfig`, `Versions`) +- [x] 60+ version pins (Istio, Karpenter, Grafana, Prometheus, Loki, Keycloak, etc.) +- [x] 8 Jinja2 templates rendering Terragrunt HCL files +- [x] Atomic multi-file commit via Git tree API (`write_files_atomic`) +- [x] Backward-compatible `config.json` always written alongside HCL files +- [x] Generic mode (single `config.json`) still supported + +### Phase 3: Testing ✅ COMPLETE +- [x] `scripts/test_workflow.py` — end-to-end test with `--eks` flag +- [x] `scripts/check_github_permissions.py` — 11-check PAT audit +- [x] `scripts/validate_github_token.py` — token retrieval + format validation +- [x] `scripts/cleanup_test_repos.py` — test repo cleanup +- [x] Unit tests in `tests/` +- [x] Successful E2E tests: both generic and EKS modes pass ✅ + +### Phase 4: Documentation ✅ COMPLETE +- [x] `README.md` — comprehensive project overview +- [x] `DEPLOYMENT.md` — step-by-step deployment guide with Service Catalog launch instructions +- [x] `CLOUDFORMATION_CUSTOM_RESOURCE_GUIDE.md` — Custom Resource integration guide +- [x] `service-catalog/product-template.yaml` — production EKS product template +- [x] `docs/SERVICE_CATALOG_RESOLUTION.md` — S3 tag-based access fix +- [x] Design docs (this file, CUSTOM_TEMPLATES.MD, REPO_VARS_AND_SECRETS.md) + +## Data Flow + +``` +Service Catalog UI + │ (user fills form) + ▼ +CloudFormation Stack + │ ResourceProperties (snake_case) + ▼ +Lambda Handler (app.py) + │ + ├─ Validates with CloudFormationResourceInput (Pydantic v1) + ├─ get_provider() → GitHubProvider (or GitLabProvider) + ├─ Creates repository (internal visibility) + ├─ Creates repo-init branch + ├─ Clones template-eks-cluster contents + │ + ├─ is_eks_deployment? ─── YES ──┐ + │ ▼ + │ to_eks_deployment_config() + │ render_eks_config() → 8 RenderedFile objects + │ write_files_atomic() → Git tree API commit + │ │ + ├─ is_eks_deployment? ─── NO ───┤ + │ ▼ + │ write_file(config.json) + │ │ + ├─ Opens PR: repo-init → main │ + ├─ Sets team permissions │ + └─ send_cfn_response(SUCCESS) ──┘ + │ + ▼ + Stack Outputs: RepositoryUrl, PullRequestUrl +``` + +## Success Criteria — All Met ✅ + +- ✅ Core system processes CloudFormation Custom Resource events +- ✅ EKS deployments render full Terragrunt file hierarchy (8 files) +- ✅ Generic deployments write single config.json +- ✅ Atomic commits via Git tree API (no partial writes) +- ✅ End-to-end tests pass for both modes +- ✅ Service Catalog product is provisioned and working +- ✅ Documentation covers deployment, usage, and troubleshooting diff --git a/docs/SERVICE_CATALOG_RESOLUTION.md b/docs/SERVICE_CATALOG_RESOLUTION.md index 4891f8f7..a04376a0 100644 --- a/docs/SERVICE_CATALOG_RESOLUTION.md +++ b/docs/SERVICE_CATALOG_RESOLUTION.md @@ -78,7 +78,7 @@ After applying the changes: "servicecatalog:provisioning": "true", "ManagedBy": "Terraform", "Environment": "production", - "Purpose": "ServiceCatalogRepoGenerator" + "Purpose": "EKSTerragruntRepoGenerator" } ``` @@ -106,17 +106,24 @@ After applying the changes: ## Parameters Available -The product accepts the following parameters: +The product accepts the following parameters (EKS deployment template): | Parameter | Required | Default | Description | |-----------|----------|---------|-------------| | `ProjectName` | Yes | - | Repository name (lowercase, hyphens) | -| `OwningTeam` | Yes | `tf-module-admins` | GitHub team with admin access | -| `Environment` | Yes | `development` | Environment (development/staging/production/sandbox) | -| `AwsRegion` | No | `us-gov-west-1` | Primary AWS region | -| `OrganizationPath` | No | - | Organization path (e.g., dept:team:subteam) | -| `FinOpsProjectNumber` | No | - | FinOps project number | +| `ClusterName` | No | ProjectName | EKS cluster name | +| `OwningTeam` | No | `tf-module-admins` | GitHub team with admin access | +| `Environment` | Yes | `dev` | `dev` / `test` / `prod` | +| `AwsRegion` | Yes | `us-gov-west-1` | AWS region | +| `AccountName` | Yes | - | AWS account name (e.g., `csvd-dev-ew`) | +| `AWSAccountId` | Yes | - | 12-digit AWS account ID | +| `EnvironmentAbbr` | Yes | - | Environment abbreviation | +| `VpcName` | Yes | - | VPC name for the cluster | +| `VpcDomainName` | Yes | - | VPC domain (e.g., `dev.inf.csp1.census.gov`) | +| `ClusterMailingList` | No | - | Notification email | +| `OrganizationPath` | No | `census:ocio:csvd` | Organization path | | `FinOpsProjectName` | No | - | FinOps project name | +| `FinOpsProjectNumber` | No | - | FinOps project number | | `AdditionalTags` | No | `{}` | Additional tags as JSON | | `LambdaFunctionArn` | No | (locked) | ARN of the Lambda function (enforced by template constraint) | @@ -138,9 +145,9 @@ The product accepts the following parameters: ## Next Steps 1. ✅ Service Catalog product is ready to use -2. ⏳ Test launching a repository through the product -3. ⏳ Rebuild Lambda container image to fix stale code issue (separate task) -4. ⏳ Document end-user instructions for using the Service Catalog product +2. ✅ Tested launching a repository through the product (both generic and EKS modes) +3. ✅ Rebuilt Lambda container image with current code (EKS rendering, atomic commits) +4. ✅ Documented end-user instructions for using the Service Catalog product (see `DEPLOYMENT.md`) 5. ⏳ Set up CI/CD pipeline to automatically update product template on changes ## Acknowledgments diff --git a/events/cloudformation-create-event.json b/events/cloudformation-create-event.json index 6c4299b4..232f089a 100644 --- a/events/cloudformation-create-event.json +++ b/events/cloudformation-create-event.json @@ -1,17 +1,26 @@ { "RequestType": "Create", - "ServiceToken": "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation", + "ServiceToken": "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:eks-terragrunt-repo-gen-template-automation", "ResponseURL": "https://cloudformation-custom-resource-response-usgov-west-1.s3-us-gov-west-1.amazonaws.com/...", "StackId": "arn:aws-us-gov:cloudformation:us-gov-west-1:229685449397:stack/test-repo-stack/12345678-1234-1234-1234-123456789012", "RequestId": "unique-request-id-12345", - "LogicalResourceId": "MyTestRepository", - "ResourceType": "Custom::RepositoryCreator", + "LogicalResourceId": "RepositoryCreator", + "ResourceType": "Custom::GitHubRepository", "ResourceProperties": { - "ServiceToken": "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation", - "ProjectName": "example-test-repository", - "OwningTeam": "platform-team", - "Environment": "development", - "AwsRegion": "us-gov-west-1", - "AdditionalMetadata": "custom-value" + "ServiceToken": "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:eks-terragrunt-repo-gen-template-automation", + "project_name": "example-test-repository", + "owning_team": "tf-module-admins", + "cluster_name": "example-test-repository", + "environment": "dev", + "aws_region": "us-gov-west-1", + "account_name": "csvd-dev-ew", + "aws_account_id": "229685449397", + "environment_abbr": "dev", + "vpc_name": "csvd-dev-vpc", + "vpc_domain_name": "dev.inf.csp1.census.gov", + "cluster_mailing_list": "sct-engineering@census.gov", + "organization_path": "census:ocio:csvd", + "finops_project_name": "EKS Platform Services", + "finops_project_number": "12345" } } diff --git a/lambda-template-repo-generator.code-workspace b/lambda-template-repo-generator.code-workspace index 39cb7535..111a9755 100644 --- a/lambda-template-repo-generator.code-workspace +++ b/lambda-template-repo-generator.code-workspace @@ -15,6 +15,12 @@ { "path": "../terraform-provider-aws/website/docs", "name": "terraform-provider-aws docs" + }, + { + "path": "../terraform-github-repo" + }, + { + "path": "../terraform-eks-deployment" } ], "settings": {} diff --git a/main.tf b/main.tf index 3512139d..4378a9b0 100644 --- a/main.tf +++ b/main.tf @@ -1,7 +1,9 @@ # # This Terraform configuration creates only the ECR repository for the container image. # The Lambda function, API Gateway, IAM roles, and other infrastructure are managed by -# the terraform-aws-template-automation module. +# the terraform-aws-template-automation module (see deploy/). +# +# This ECR repo hosts the EKS Terragrunt Repository Generator Lambda image. provider "aws" { region = var.aws_region diff --git a/packer.pkr.hcl b/packer.pkr.hcl index 36daa30f..52c9a13a 100644 --- a/packer.pkr.hcl +++ b/packer.pkr.hcl @@ -1,10 +1,13 @@ -# Packer template for AWS Service Catalog Repository Generator Lambda +# Packer template for EKS Terragrunt Repository Generator Lambda # -# This builds a Lambda container image that processes AWS Service Catalog provisioning -# events to automatically create and configure GitHub/GitLab repositories from templates. +# This builds a Lambda container image that processes CloudFormation Custom +# Resource events to create and configure GitHub repositories with rendered +# EKS Terragrunt/HCL configuration files from Jinja2 templates. # -# The Lambda function is triggered by EventBridge when Service Catalog provisions a product, -# and it creates a new repository with configuration based on the provisioning parameters. +# The Lambda is triggered by CloudFormation (via Custom::GitHubRepository) +# when a user provisions a Service Catalog product. +# +# Build: packer-pipeline --config config_packer.hcl --wait packer { required_plugins { @@ -22,7 +25,7 @@ packer { variable "repository_uri" { type = string default = "" - description = "ECR repository URI for the Service Catalog Lambda Docker image" + description = "ECR repository URI for the EKS Terragrunt Repo Generator Lambda Docker image" } variable "tag" { @@ -39,7 +42,7 @@ variable "terraform_version" { variable "base_image" { type = string - default = "229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/service-catalog-repo-generator/lambda-python:3.11" + default = "229685449397.dkr.ecr.us-gov-west-1.amazonaws.com/eks-terragrunt-repo-generator/lambda-python:3.11" description = "Base AWS Lambda Python image for building the container (using cloned private ECR image)" } @@ -72,7 +75,7 @@ source "docker" "lambda" { } build { - name = "service-catalog-repo-generator" + name = "eks-terragrunt-repo-generator" sources = [ "source.docker.lambda" @@ -95,7 +98,7 @@ build { destination = "/var/task" } - # Install Python dependencies for Service Catalog event handling + # Install Python dependencies for EKS Terragrunt repo generation provisioner "shell" { inline = [ "cd /var/task", @@ -103,9 +106,9 @@ build { "cp /etc/pip-cert.pem /etc/pki/ca-trust/source/anchors/census-root-ca.pem || true", "update-ca-trust extract || true", "cat /etc/pip-cert.pem >> /var/lang/lib/python3.11/site-packages/certifi/cacert.pem || true", - "echo 'Installing dependencies for Service Catalog Lambda function...'", + "echo 'Installing dependencies for EKS Terragrunt Repo Generator Lambda...'", "pip install -r requirements.txt", - "echo 'Lambda function built for AWS Service Catalog integration'" + "echo 'Lambda function built for EKS Terragrunt repository generation via Service Catalog'" ] } diff --git a/scripts/check_github_permissions.py b/scripts/check_github_permissions.py index 05ec957d..cc884228 100644 --- a/scripts/check_github_permissions.py +++ b/scripts/check_github_permissions.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -GitHub PAT permissions checker for the Service Catalog Repository Creator. +GitHub PAT permissions checker for the EKS Terragrunt Repository Generator. Verifies that the token stored in AWS Secrets Manager (or supplied directly) has every permission required by the Lambda workflow: @@ -74,7 +74,7 @@ # ── shared constants (mirror test_workflow.py) ──────────────────────────────── REGION = "us-gov-west-1" -FUNCTION_NAME = "service-catalog-repo-gen-template-automation" +FUNCTION_NAME = "eks-terragrunt-repo-gen-template-automation" DEFAULT_SECRET = "/eks-cluster-deployment/github_token" DEFAULT_API = "https://github.e.it.census.gov" DEFAULT_ORG = "SCT-Engineering" @@ -682,7 +682,7 @@ def render_summary(results: list[CheckResult]) -> None: def main() -> None: parser = argparse.ArgumentParser( - description="Verify GitHub PAT permissions for the Service Catalog repo creator Lambda.", + description="Verify GitHub PAT permissions for the EKS Terragrunt repo generator Lambda.", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("--token", default="", help="PAT to test (default: from Secrets Manager)") diff --git a/scripts/test_workflow.py b/scripts/test_workflow.py index bda95722..5204e545 100644 --- a/scripts/test_workflow.py +++ b/scripts/test_workflow.py @@ -13,8 +13,10 @@ Usage ----- python scripts/test_workflow.py [REPO_NAME] + python scripts/test_workflow.py --eks [REPO_NAME] - REPO_NAME defaults to "workflow-test-" + --eks Trigger the EKS deployment rendering path (multi-file commit) + REPO_NAME defaults to "workflow-test-" (or "eks-test-" with --eks) """ import json @@ -42,7 +44,7 @@ # --------------------------------------------------------------------------- REGION = "us-gov-west-1" -FUNCTION_NAME = "service-catalog-repo-gen-template-automation" +FUNCTION_NAME = "eks-terragrunt-repo-gen-template-automation" SECRET_NAME_ENV_KEY = "GITHUB_TOKEN_SECRET_NAME" DEFAULT_SECRET_PATH = "/eks-cluster-deployment/github_token" LOG_GROUP = f"/aws/lambda/{FUNCTION_NAME}" @@ -281,7 +283,36 @@ def _check_github(config: LambdaEnvConfig, token: str) -> tuple[StepResult, GitH ), gc -def _build_cfn_payload(repo_name: str, lambda_arn: str, stack_id: str, request_id: str) -> dict: +def _build_cfn_payload(repo_name: str, lambda_arn: str, stack_id: str, request_id: str, eks: bool = False) -> dict: + resource_properties: dict[str, str] = { + "ServiceToken": lambda_arn, + "project_name": repo_name, + "owning_team": "tf-module-admins", + } + + if eks: + # EKS deployment parameters – triggers the EKS rendering path in the Lambda + resource_properties.update({ + "cluster_name": repo_name, + "environment": "dev", + "aws_region": REGION, + "account_name": "csvd-dev-ew", + "aws_account_id": "229685449397", + "environment_abbr": "dev", + "vpc_name": "csvd-dev-vpc", + "vpc_domain_name": "dev.inf.csp1.census.gov", + "cluster_mailing_list": "sct-engineering@census.gov", + "organization_path": "census:ocio:csvd", + "finops_project_name": "EKS Platform Services", + "finops_project_number": "12345", + }) + else: + # Legacy / non-EKS test + resource_properties.update({ + "environment": "development", + "aws_region": REGION, + }) + return { "RequestType": "Create", "ResponseURL": "https://httpbin.org/put", # dummy – will time out but won't block Lambda @@ -289,17 +320,11 @@ def _build_cfn_payload(repo_name: str, lambda_arn: str, stack_id: str, request_i "RequestId": request_id, "ResourceType": "Custom::GitHubRepository", "LogicalResourceId": "TestRepository", - "ResourceProperties": { - "ServiceToken": lambda_arn, - "ProjectName": repo_name, - "OwningTeam": "tf-module-admins", - "Environment": "development", - "AwsRegion": REGION, - }, + "ResourceProperties": resource_properties, } -def _invoke_lambda(session: boto3.Session, repo_name: str) -> tuple[StepResult, LambdaInvokeResult]: +def _invoke_lambda(session: boto3.Session, repo_name: str, eks: bool = False) -> tuple[StepResult, LambdaInvokeResult]: lc = session.client("lambda", region_name=REGION) # Get Lambda ARN for the payload ServiceToken @@ -311,8 +336,9 @@ def _invoke_lambda(session: boto3.Session, repo_name: str) -> tuple[StepResult, stack_id = f"arn:aws-us-gov:cloudformation:{REGION}:229685449397:stack/test-wf-{repo_name}/test-id" request_id = str(uuid.uuid4()) - payload = _build_cfn_payload(repo_name, lambda_arn, stack_id, request_id) + payload = _build_cfn_payload(repo_name, lambda_arn, stack_id, request_id, eks=eks) + mode_label = "EKS deployment" if eks else "legacy" try: resp = lc.invoke( FunctionName = FUNCTION_NAME, @@ -325,6 +351,7 @@ def _invoke_lambda(session: boto3.Session, repo_name: str) -> tuple[StepResult, passed = accepted, detail = ( f"Repo name: {repo_name}\n" + f"Mode: {mode_label}\n" f"Request ID: {request_id}\n" f"HTTP status: {resp['StatusCode']}" ), @@ -564,11 +591,25 @@ def render_summary(steps: list[StepResult], invoke: Optional[LambdaInvokeResult] # --------------------------------------------------------------------------- def main() -> None: - repo_name = sys.argv[1] if len(sys.argv) > 1 else f"workflow-test-{int(time.time())}" - - console.print(Rule("[bold cyan]Service Catalog Repo Generator – Workflow Test[/bold cyan]")) + import argparse + parser = argparse.ArgumentParser(description="End-to-end workflow tester") + parser.add_argument("repo_name", nargs="?", default=None, help="Repository name to create") + parser.add_argument("--eks", action="store_true", help="Test EKS deployment path (multi-file commit)") + args = parser.parse_args() + + eks_mode = args.eks + if args.repo_name: + repo_name = args.repo_name + else: + prefix = "eks-test" if eks_mode else "workflow-test" + repo_name = f"{prefix}-{int(time.time())}" + + mode_label = "[bold magenta]EKS deployment[/bold magenta]" if eks_mode else "[bold]legacy[/bold]" + + console.print(Rule("[bold cyan]EKS Terragrunt Repo Generator – Workflow Test[/bold cyan]")) console.print(f"[dim]Timestamp : {datetime.now(timezone.utc).isoformat()}[/dim]") console.print(f"[dim]Test repo : [bold]{repo_name}[/bold][/dim]") + console.print(f"[dim]Mode : {mode_label}[/dim]") console.print(f"[dim]Lambda : {FUNCTION_NAME}[/dim]") console.print(f"[dim]Region : {REGION}[/dim]\n") @@ -615,7 +656,7 @@ def main() -> None: # ── Step 5: Lambda invocation (async) ───────────────────────────────────── console.print(Rule("Step 5 · Lambda Invocation")) - inv_step, invoke_result = _invoke_lambda(session, repo_name) + inv_step, invoke_result = _invoke_lambda(session, repo_name, eks=eks_mode) steps.append(inv_step) render_step(inv_step) if not inv_step.passed: diff --git a/service-catalog/product-template.yaml b/service-catalog/product-template.yaml index 6d0b3749..6b0ca503 100644 --- a/service-catalog/product-template.yaml +++ b/service-catalog/product-template.yaml @@ -171,8 +171,8 @@ Parameters: # Hidden parameter - the Lambda ARN is passed in from the Service Catalog product definition LambdaFunctionArn: Type: String - Description: ARN of the Lambda function that creates repositories - Default: "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:service-catalog-repo-gen-template-automation" + Description: ARN of the Lambda function that creates EKS cluster repositories + Default: "arn:aws-us-gov:lambda:us-gov-west-1:229685449397:function:eks-terragrunt-repo-gen-template-automation" Conditions: ClusterNameProvided: !Not diff --git a/template_automation/__pycache__/app.cpython-311.pyc b/template_automation/__pycache__/app.cpython-311.pyc index 1daae22ffc8df13fc57d35e97a0846cf33a6ca9c..084cb9c7fb89c9f033a8805aa1dfd8a30d819eff 100644 GIT binary patch delta 12446 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π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 Ut)H)7rto>ISAPC(C~d_4KQ{z*Hvj+t delta 7188 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 diff --git a/template_automation/__pycache__/eks_config.cpython-311.pyc b/template_automation/__pycache__/eks_config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41ee57d410416714ea093484771eb0eebf096df2 GIT binary patch literal 21867 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 pFmWhNZoyv`)wlbD;7lBipf nnp=>QSdyw+lvJZRDEF#;CUW5?izaVgsgsyGR+ diff --git a/template_automation/__pycache__/repository_provider.cpython-311.pyc b/template_automation/__pycache__/repository_provider.cpython-311.pyc index 22006e5636c1ead2d4630f06b706585bf5ea0e7a..827e37f5d7962ff904de48fb18e17b2a19917e6a 100644 GIT binary patch delta 3815 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 Qc`bIAYwkTp&u_;60hbeF8~^|S diff --git a/varfiles/default.tfvars b/varfiles/default.tfvars index 08e90254..303d4a7a 100644 --- a/varfiles/default.tfvars +++ b/varfiles/default.tfvars @@ -1,16 +1,16 @@ aws_region = "us-east-1" -repository_name = "template-automation-lambda" +repository_name = "eks-terragrunt-repo-generator" catalog_data = { - about_text = "Template Automation Lambda Image" + about_text = "EKS Terragrunt Repository Generator Lambda Image" architectures = ["x86_64"] - description = "Lambda container image for template automation" + description = "Lambda container image for creating EKS cluster repos with rendered Terragrunt/HCL configuration" operating_systems = ["AmazonLinux2"] - usage_text = "Creates a Template Automation Lambda container image" + usage_text = "Creates an EKS Terragrunt Repo Generator Lambda container image" } tags = { env = "production" managed_by = "terraform" - project = "template-automation" + project = "eks-terragrunt-repo-generator" } diff --git a/variables.tf b/variables.tf index 53651e9e..9b44f8c8 100644 --- a/variables.tf +++ b/variables.tf @@ -15,7 +15,7 @@ variable "aws_region" { variable "repository_name" { description = "Name of the ECR public repository" type = string - default = "template-automation-lambda" + default = "eks-terragrunt-repo-generator" } variable "environment" {