Skip to content

Commit b9e80ab

Browse files
feat: [PMM-14303] OpenShift cluster cleanup Lambda automation
1 parent 1b65d67 commit b9e80ab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+4944
-0
lines changed

IaC/cdk/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# CDK Projects
2+
3+
AWS CDK infrastructure as code for automated resource management.
4+
5+
## openshift-resources-cleanup
6+
7+
Automated Lambda that cleans up expired OpenShift/ROSA clusters. Scans all AWS regions every 15 minutes, validates TTL tags, and deletes cluster infrastructure (VPC, ELB, Route53, S3, EC2).
8+
9+
```bash
10+
cd openshift-resources-cleanup
11+
just install && just bootstrap
12+
just deploy # Deploy LIVE mode
13+
just logs # View logs
14+
```
15+
16+
See [openshift-resources-cleanup/README.md](openshift-resources-cleanup/README.md) for full documentation.
17+
18+
## Requirements
19+
20+
- AWS CLI configured
21+
- `brew install uv just`
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# CDK
2+
cdk.out/
3+
.cdk.staging/
4+
cdk.context.json
5+
6+
# Python
7+
__pycache__/
8+
*.py[cod]
9+
*$py.class
10+
*.so
11+
.Python
12+
build/
13+
develop-eggs/
14+
dist/
15+
downloads/
16+
eggs/
17+
.eggs/
18+
lib/
19+
lib64/
20+
parts/
21+
sdist/
22+
var/
23+
wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
MANIFEST
28+
29+
# Virtual environments
30+
venv/
31+
ENV/
32+
env/
33+
.venv
34+
35+
# Testing
36+
.pytest_cache/
37+
.coverage
38+
htmlcov/
39+
.tox/
40+
41+
# IDEs
42+
.vscode/
43+
.idea/
44+
*.swp
45+
*.swo
46+
*~
47+
48+
# OS
49+
.DS_Store
50+
Thumbs.db
51+
52+
# Lambda artifacts
53+
*.zip
54+
/tmp/
55+
56+
# Lambda dependencies (installed at build time)
57+
lambda/*.dist-info/
58+
lambda/bin/
59+
lambda/boto3/
60+
lambda/boto3-*.dist-info/
61+
lambda/botocore/
62+
lambda/botocore-*.dist-info/
63+
lambda/aws_lambda_powertools/
64+
lambda/aws_lambda_powertools-*.dist-info/
65+
lambda/aws_xray_sdk/
66+
lambda/aws_xray_sdk-*.dist-info/
67+
lambda/s3transfer/
68+
lambda/s3transfer-*.dist-info/
69+
lambda/jmespath/
70+
lambda/jmespath-*.dist-info/
71+
lambda/dateutil/
72+
lambda/python_dateutil-*.dist-info/
73+
lambda/urllib3/
74+
lambda/urllib3-*.dist-info/
75+
lambda/six.py
76+
lambda/six-*.dist-info/
77+
lambda/typing_extensions.py
78+
lambda/typing_extensions-*.dist-info/
79+
lambda/wrapt/
80+
lambda/wrapt-*.dist-info/
81+
lambda/.lock
82+
83+
# Logs
84+
*.log
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# OpenShift Cluster Cleanup
2+
3+
Automated Lambda that cleans up expired OpenShift/ROSA clusters. Runs every 15 minutes, scans for expired clusters, deletes all resources (VPC, ELB, Route53, S3, EC2).
4+
5+
## Configuration
6+
7+
| Setting | Default | How to Change |
8+
|---------|---------|---------------|
9+
| **Lambda Location** | `us-east-2` | `AWS_REGION=us-west-1 just deploy` |
10+
| **Scan Regions** (comma-separated) | `all` | `just deploy us-east-2` or `just deploy us-east-1,eu-west-1,ap-south-1` |
11+
| **AWS Profile** | `default` | `AWS_PROFILE=myprofile just deploy` |
12+
| **Mode** | `LIVE` | `just deploy-dry` |
13+
14+
## Quick Start
15+
16+
```bash
17+
brew install uv just && aws configure
18+
cd IaC/cdk/openshift-resources-cleanup
19+
just install && just bootstrap
20+
just deploy # Scans all regions (default)
21+
just deploy us-east-2 # Scans us-east-2 only
22+
AWS_REGION=us-west-1 just deploy # Deploy Lambda to us-west-1
23+
```
24+
25+
## Commands
26+
27+
```bash
28+
just deploy [regions] # Deploy LIVE (default: all regions)
29+
just deploy-dry [regions]# Deploy DRY_RUN (toggles LIVE off)
30+
just logs # Tail CloudWatch logs
31+
just params # Show configuration
32+
just test # Run tests
33+
```
34+
35+
Run `just` for all commands.
36+
37+
## How It Works
38+
39+
1. **Detect**: Scans EC2 for OpenShift/ROSA clusters (tags: `red-hat-clustertype: rosa` or name: `*-master-*`)
40+
2. **Check TTL**: Reads `creation-time` + `delete-cluster-after-hours` tags, skips if not expired
41+
3. **Delete**: Removes all resources in dependency order (instances → ELB → NAT → VPC → Route53 → S3)
42+
43+
## Logs & Troubleshooting
44+
45+
```bash
46+
just logs # View real-time logs
47+
just params # Check configuration
48+
```
49+
50+
**Example output:**
51+
```
52+
Detected OpenShift ROSA cluster: jvp-rosa1-qmdkk
53+
Cluster TTL not expired (5.45 hours remaining)
54+
```
55+
56+
**Common issues:** Missing TTL tags (`creation-time`, `delete-cluster-after-hours`), `DryRunMode=true`, permissions
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python3
2+
"""CDK app for OpenShift Cluster Cleanup Lambda."""
3+
4+
import os
5+
import aws_cdk as cdk
6+
from stacks.resource_cleanup_stack import ResourceCleanupStack
7+
8+
# Stack name (single source of truth)
9+
STACK_NAME = "OpenShiftResourcesCleanupStack"
10+
11+
app = cdk.App()
12+
13+
ResourceCleanupStack(
14+
app,
15+
STACK_NAME,
16+
description="OpenShift cluster infrastructure cleanup for AWS",
17+
env=cdk.Environment(
18+
account=os.getenv('CDK_DEFAULT_ACCOUNT'),
19+
region=os.getenv('CDK_DEFAULT_REGION', 'us-east-2')
20+
),
21+
tags={
22+
"Project": "PlatformEngineering",
23+
"ManagedBy": "CDK",
24+
"iit-billing-tag": "openshift-cleanup"
25+
}
26+
)
27+
28+
app.synth()
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{
2+
"app": "python3 app.py",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"requirements*.txt",
11+
"source.bat",
12+
"**/__pycache__",
13+
"**/*.pyc",
14+
".pytest_cache"
15+
]
16+
},
17+
"context": {
18+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
19+
"@aws-cdk/core:checkSecretUsage": true,
20+
"@aws-cdk/core:target-partitions": [
21+
"aws",
22+
"aws-cn"
23+
],
24+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
25+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
26+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
27+
"@aws-cdk/aws-iam:minimizePolicies": true,
28+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
29+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
30+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
31+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
32+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
33+
"@aws-cdk/core:enablePartitionLiterals": true,
34+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
35+
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
36+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
37+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
38+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
39+
"@aws-cdk/aws-route53-patternslibrary:useCertificate": true,
40+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
41+
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
42+
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
43+
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
44+
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
45+
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
46+
"@aws-cdk/aws-redshift:columnId": true,
47+
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
48+
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
49+
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
50+
"@aws-cdk/aws-kms:aliasNameRef": true,
51+
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
52+
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
53+
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
54+
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
55+
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
56+
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
57+
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
58+
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
59+
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
60+
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
61+
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
62+
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
63+
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
64+
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
65+
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
66+
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
67+
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
68+
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
69+
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
70+
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
71+
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
72+
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
73+
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
74+
"@aws-cdk/aws-dynamodb:retainTableReplica": true,
75+
"@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration": false,
76+
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
77+
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
78+
"@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true,
79+
"@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true,
80+
"@aws-cdk/aws-ecs:disableEcsImdsBlocking": true,
81+
"@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false,
82+
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
83+
"@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true,
84+
"@aws-cdk/aws-events:requireEventBusPolicySid": true,
85+
"@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true,
86+
"@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": true,
87+
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
88+
"@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": false,
89+
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
90+
"@aws-cdk/aws-lambda:useCdkManagedLogGroup": true,
91+
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
92+
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
93+
"@aws-cdk/aws-route53-patters:useCertificate": true,
94+
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true,
95+
"@aws-cdk/aws-s3:publicAccessBlockedByDefault": true,
96+
"@aws-cdk/aws-s3:setUniqueReplicationRoleName": true,
97+
"@aws-cdk/aws-signer:signingProfileNamePassedToCfn": true,
98+
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
99+
"@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask": true,
100+
"@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true,
101+
"@aws-cdk/cognito:logUserPoolClientSecretValue": false,
102+
"@aws-cdk/core:aspectPrioritiesMutating": true,
103+
"@aws-cdk/core:aspectStabilization": true,
104+
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
105+
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
106+
"@aws-cdk/core:explicitStackTags": true,
107+
"@aws-cdk/core:newStyleStackSynthesis": true,
108+
"@aws-cdk/core:stackRelativeExports": true,
109+
"@aws-cdk/pipelines:reduceAssetRoleTrustScope": true,
110+
"@aws-cdk/pipelines:reduceCrossAccountActionRoleTrustScope": true,
111+
"@aws-cdk/pipelines:reduceStageRoleTrustScope": true,
112+
"@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true
113+
}
114+
}

0 commit comments

Comments
 (0)