This repository was archived by the owner on Jan 6, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeploy.sh
More file actions
699 lines (596 loc) · 27.6 KB
/
deploy.sh
File metadata and controls
699 lines (596 loc) · 27.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
#!/bin/bash
#################################################################################
# File: deploy.sh
# Description: Main deployment script (Bash) for AWS DevSecOps Hybrid CI/CD Platform
# Author: Haitam Bidiouane (@sch0penheimer)
# Last Modified: 27/09/2025
#
# This script orchestrates the complete deployment:
# 1. Validates environment configuration
# 2. Creates Lambda ZIP package
# 3. Optionally deploys Terraform infrastructure
# 4. Deploys CloudFormation CI/CD pipeline with Terraform outputs
#################################################################################
set -e
##-- Script config --##
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
TERRAFORM_DIR="$ROOT_DIR/terraform-manifests"
CLOUDFORMATION_DIR="$ROOT_DIR/cloudformation"
ENV_FILE="$ROOT_DIR/.env"
##-- Colors (output) --##
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
NOCOLOR='\033[0m'
##-- CLI options --##
SKIP_INFRASTRUCTURE=false
ROLLBACK_DEPLOYMENT=false
SHOW_HELP=false
##-- CLI arguments parsing --##
while [[ $# -gt 0 ]]; do
case $1 in
--skip-infrastructure)
SKIP_INFRASTRUCTURE=true
shift
;;
--rollback-deployment )
ROLLBACK_DEPLOYMENT=true
shift
;;
--help|-h)
SHOW_HELP=true
shift
;;
*)
echo "Unknown option $1"
exit 1
;;
esac
done
show_help() {
echo -e "${CYAN}AWS DevSecOps Hybrid CI/CD Platform Deployment Script Helping Manual${NOCOLOR}"
echo
echo -e "${CYAN}USAGE:${NOCOLOR}"
echo " $0 [OPTIONS]"
echo
echo -e "${CYAN}OPTIONS:${NOCOLOR}"
echo " --skip-infrastructure Skip Terraform infrastructure deployment (use existing infrastructure)"
echo " --rollback-deployment Rollback existing Terraform infrastructure & CloudFormation stack and exit"
echo " --help, -h Show this help message"
echo
echo -e "${CYAN}EXAMPLES:${NOCOLOR}"
echo -e " $0 ${BLUE}#- Full deployment with new infrastructure -#${NOCOLOR}"
echo -e " $0 --skip-infrastructure ${BLUE}#- Deploy only CI/CD pipeline to existing infrastructure -#${NOCOLOR}"
echo -e " $0 --rollback-deployment ${BLUE}#- Rollback deployment and exit -#${NOCOLOR}"
echo
echo -e "${CYAN}PREREQUISITES:${NOCOLOR}"
echo " - Bash v4.0+"
echo " - AWS CLI v2+"
echo " - Terraform v1.0+"
echo " - jq (JSON processor)"
echo " - zip & unzip (for Lambda packaging)"
echo " - .env file completed with required configuration"
echo
echo -e "${CYAN}NOTES:${NOCOLOR}"
echo " - All prerequisites MUST be installed before running this script"
echo " - Supports Windows (Git Bash/WSL), macOS, and Linux"
echo " - Script will exit if any prerequisites are missing"
}
##-- Logger function (w/ timestamp & color coding) --##
log_message() {
local message="$1"
local type="${2:-INFO}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case $type in
"ERROR") echo -e "[$timestamp: ${RED}ERROR${NOCOLOR}] $message" ;;
"WARNING") echo -e "[$timestamp: ${YELLOW}WARNING${NOCOLOR}] $message" ;;
"SUCCESS") echo -e "[$timestamp: ${GREEN}SUCCESS${NOCOLOR}] $message" ;;
"INFO") echo -e "[$timestamp: ${CYAN}INFO${NOCOLOR}] $message" ;;
"DEBUG") echo -e "[$timestamp: ${PURPLE}DEBUG${NOCOLOR}] $message" ;;
*) echo -e "[$timestamp: ${BLUE}LOG${NOCOLOR}] $message" ;;
esac
}
##-- Function to detect OS --##
detect_os() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "linux"
elif [[ "$OSTYPE" == "darwin"* ]]; then
echo "macos"
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
echo "windows"
else
echo "unknown"
fi
}
##-- Prerequisites check (validation only) --##
check_prerequisites() {
log_message "Checking prerequisites:" "INFO"
local missing_tools=()
local all_good=true
#- Check AWS CLI -#
if ! command -v aws &> /dev/null; then
log_message "AWS CLI not found" "ERROR"
missing_tools+=("AWS CLI")
all_good=false
else
local aws_version=$(aws --version 2>&1 | cut -d/ -f2 | cut -d' ' -f1)
log_message "AWS CLI found: v$aws_version" "SUCCESS"
fi
#- Check Terraform -#
if ! command -v terraform &> /dev/null; then
log_message "Terraform not found" "ERROR"
missing_tools+=("Terraform")
all_good=false
else
local tf_version=$(terraform version -json 2>/dev/null | jq -r '.terraform_version' 2>/dev/null || terraform version | head -n1 | cut -d' ' -f2)
log_message "Terraform found: $tf_version" "SUCCESS"
#- Check minimum Terraform version (1.0+) -#
local tf_major_version=$(echo "$tf_version" | cut -d'.' -f1 | sed 's/v//')
if [[ "$tf_major_version" -lt 1 ]]; then
log_message "Terraform version $tf_version is too old. Required: v1.0+" "ERROR"
missing_tools+=("Terraform v1.0+")
all_good=false
fi
fi
#- Check jq -#
if ! command -v jq &> /dev/null; then
log_message "jq not found (required for parsing Terraform outputs)" "ERROR"
missing_tools+=("jq")
all_good=false
else
local jq_version=$(jq --version 2>/dev/null || echo "jq-unknown")
log_message "jq found: $jq_version" "SUCCESS"
fi
#- Check unzip (required for Lambda packaging) -#
if ! command -v unzip &> /dev/null; then
log_message "unzip not found (required for Lambda packaging)" "ERROR"
missing_tools+=("unzip")
all_good=false
else
log_message "unzip found" "SUCCESS"
fi
#- Check Bash version -#
if [[ ${BASH_VERSION%%.*} -lt 4 ]]; then
log_message "Bash version ${BASH_VERSION} is too old. Required: 4.0+" "ERROR"
missing_tools+=("Bash 4.0+")
all_good=false
else
log_message "Bash version: ${BASH_VERSION}" "SUCCESS"
fi
#- Check zip (required for Lambda packaging) -#
if ! command -v zip &> /dev/null; then
log_message "zip not found (required for Lambda packaging)" "ERROR"
missing_tools+=("zip")
all_good=false
else
log_message "zip found" "SUCCESS"
fi
#- Check .env file -#
if [[ ! -f "$ENV_FILE" ]]; then
log_message " .env file not found at: $ENV_FILE" "ERROR"
missing_tools+=(".env configuration file")
all_good=false
else
log_message ".env file found" "SUCCESS"
fi
#- Exit if any prerequisites are missing -#
if [[ "$all_good" == false ]]; then
log_message "PREREQUISITES CHECK FAILED" "ERROR"
log_message "Missing tools/requirements: ${missing_tools[*]}" "ERROR"
log_message "Please install the missing prerequisites and re-run the script." "WARNING"
exit 1
fi
log_message "All prerequisites check completed successfully" "SUCCESS"
}
##-- Function to configure AWS credentials --##
configure_aws_credentials() {
log_message "Configuring AWS credentials:" "INFO"
#- Check if credentials are already available via AWS credential chain -#
if aws sts get-caller-identity &> /dev/null; then
local aws_identity=$(aws sts get-caller-identity --query 'Arn' --output text 2>/dev/null)
local account_id=$(aws sts get-caller-identity --query 'Account' --output text 2>/dev/null)
log_message "Using existing AWS credentials: $aws_identity" "SUCCESS"
log_message "AWS Account ID: $account_id" "INFO"
return 0
fi
#- If no credentials found, check .env file -#
if [[ -n "$AWS_ACCESS_KEY_ID" && -n "$AWS_SECRET_ACCESS_KEY" ]]; then
log_message "Using AWS credentials from .env file" "INFO"
export AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY"
if [[ -n "$AWS_REGION" ]]; then
export AWS_DEFAULT_REGION="$AWS_REGION"
fi
#- Verify credentials work -#
if aws sts get-caller-identity &> /dev/null; then
local aws_identity=$(aws sts get-caller-identity --query 'Arn' --output text 2>/dev/null)
local account_id=$(aws sts get-caller-identity --query 'Account' --output text 2>/dev/null)
log_message "AWS credentials from .env verified: $aws_identity" "SUCCESS"
log_message "AWS Account ID: $account_id" "INFO"
else
log_message "AWS credentials from .env are invalid!" "ERROR"
log_message "Please check your AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in .env file" "ERROR"
exit 1
fi
else
log_message "No AWS credentials found!" "ERROR"
log_message "Please use one of the following methods:" "INFO"
log_message "1. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in .env file" "INFO"
log_message "2. Run 'aws configure' to set up AWS CLI profile" "INFO"
log_message "3. Use IAM roles if running on AWS infrastructure" "INFO"
log_message "4. Configure AWS SSO: 'aws configure sso'" "INFO"
exit 1
fi
}
load_environment() {
log_message "Loading environment configuration:" "INFO"
if [[ -f "$ENV_FILE" ]]; then
#- Export variables from .env file -#
set -a
source "$ENV_FILE"
set +a
log_message "Environment configuration loaded successfully" "SUCCESS"
else
log_message ".env file not found!" "ERROR"
exit 1
fi
#- Validate required environment variables -#
local required_vars=(
"GIT_PROVIDER_TYPE" "FULL_GIT_REPOSITORY_ID" "BRANCH_NAME"
"SNYK_API_KEY" "PIPELINE_NOTIFICATION_MAIL" "PIPELINE_MANUAL_APPROVER_MAIL"
"AWS_REGION" "DOCKERHUB_USERNAME" "DOCKERHUB_PASSWORD"
)
log_message "Validating required environment variables:" "INFO"
local missing_vars=()
for var in "${required_vars[@]}"; do
if [[ -z "${!var}" ]]; then
log_message " Required environment variable $var is not set!" "ERROR"
missing_vars+=("$var")
else
log_message "$var is set" "DEBUG"
fi
done
if [[ ${#missing_vars[@]} -gt 0 ]]; then
log_message " Missing required environment variables: ${missing_vars[*]}" "ERROR"
log_message "Please update your .env file with the missing variables" "ERROR"
exit 1
fi
#- AWS credentials in .env (can use other methods) -#
if [[ -n "$AWS_ACCESS_KEY_ID" || -n "$AWS_SECRET_ACCESS_KEY" ]]; then
if [[ -z "$AWS_ACCESS_KEY_ID" || -z "$AWS_SECRET_ACCESS_KEY" ]]; then
log_message " Both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be provided together" "ERROR"
exit 1
fi
log_message "AWS credentials found in .env file" "SUCCESS"
else
log_message "No AWS credentials in .env file - will use AWS credential chain" "INFO"
fi
log_message "Environment validation completed successfully" "SUCCESS"
}
create_lambda_package() {
log_message "Creating Lambda ZIP package:" "INFO"
local zip_script="$SCRIPT_DIR/zip_lambda.sh"
if [[ -f "$zip_script" ]]; then
chmod +x "$zip_script"
log_message "Executing Lambda packaging script:" "INFO"
if "$zip_script"; then
log_message "Lambda packaging completed successfully" "SUCCESS"
#- Verify -#
local lambda_zip_path="$TERRAFORM_DIR/storage/lambda.zip"
if [[ -f "$lambda_zip_path" ]]; then
local zip_size=$(du -h "$lambda_zip_path" | cut -f1)
log_message "Lambda ZIP created: $lambda_zip_path ($zip_size)" "SUCCESS"
else
log_message "Lambda ZIP file not found at expected location: $lambda_zip_path" "ERROR"
exit 1
fi
else
log_message "Lambda packaging script failed with exit code: $?" "ERROR"
log_message "Check the zip_lambda.sh script for errors" "ERROR"
exit 1
fi
else
log_message "Lambda ZIP script not found at: $zip_script" "ERROR"
exit 1
fi
}
deploy_infrastructure() {
log_message "Deploying Terraform infrastructure:" "INFO"
cd "$TERRAFORM_DIR"
log_message "Initializing Terraform:" "INFO"
terraform init
#- Validate Terraform config -#
log_message "Validating Terraform configuration:" "INFO"
terraform validate
#- Plan deployment -#
log_message "Planning Terraform deployment:" "INFO"
terraform plan -out=tfplan
#- Apply deployment -#
log_message "Applying Terraform deployment:" "WARNING"
terraform apply tfplan
log_message "Terraform infrastructure deployed successfully" "SUCCESS"
cd "$ROOT_DIR"
}
destroy_infrastructure() {
log_message "Destroying Terraform infrastructure:" "WARNING"
cd "$TERRAFORM_DIR"
echo
log_message "WARNING: This will destroy everything deployed : Terraform-managed infrastructure + CloudFormation Stack" "ERROR"
read -p "Are you absolutely sure? Type 'DESTROY' to continue: " confirmation
if [[ "$confirmation" != "DESTROY" ]]; then
log_message "Destruction cancelled by user" "INFO"
cd "$ROOT_DIR"
exit 0
fi
log_message "Proceeding with infrastructure destruction..." "INFO"
if terraform destroy -auto-approve; then
log_message "Terraform infrastructure destroyed successfully" "SUCCESS"
#- Clean up state files -#
log_message "Cleaning up Terraform state files" "INFO"
rm -f terraform.tfstate terraform.tfstate.backup
rm -f .terraform.lock.hcl
rm -f tfplan
rm -rf .terraform/
log_message "Terraform directory cleaned up" "INFO"
log_message "State cleanup completed - ready for fresh deployment" "SUCCESS"
else
log_message "Terraform destruction failed!" "ERROR"
log_message "You may need to manually clean up resources in AWS Console" "WARNING"
cd "$ROOT_DIR"
exit 1
fi
cd "$ROOT_DIR"
}
get_terraform_outputs() {
log_message "Retrieving Terraform outputs:" "INFO"
cd "$TERRAFORM_DIR"
#- Check if terraform state exists -#
if [[ ! -f "terraform.tfstate" ]]; then
log_message "No Terraform state file found - infrastructure may not be deployed" "ERROR"
cd "$ROOT_DIR"
exit 1
fi
#- Check if state has resources -#
local resource_count=$(terraform show -json 2>/dev/null | jq -r '[.values.root_module.child_modules[]?.resources // [] | length] | add // 0' 2>/dev/null || echo "0")
if [[ "$resource_count" == "0" ]]; then
log_message "Terraform state exists but no resources found" "ERROR"
cd "$ROOT_DIR"
exit 1
fi
#- Generate outputs JSON file -#
local outputs_file="/tmp/terraform_outputs.json"
log_message "Generating Terraform outputs JSON..." "INFO"
if terraform output -json > "$outputs_file" 2>&1; then
if [[ -s "$outputs_file" ]]; then
local output_count=$(jq 'keys | length' "$outputs_file" 2>/dev/null || echo "0")
log_message "Terraform outputs retrieved successfully ($output_count outputs)" "SUCCESS"
#- Debug: Show found outputs -#
log_message "Available outputs:" "DEBUG"
jq -r 'keys[]' "$outputs_file" 2>/dev/null | while read -r key; do
log_message " - $key" "DEBUG"
done
cd "$ROOT_DIR"
else
log_message "Terraform outputs file is empty" "ERROR"
log_message "This usually means no outputs are defined or infrastructure is not deployed" "ERROR"
cd "$ROOT_DIR"
exit 1
fi
else
log_message "Failed to retrieve Terraform outputs:" "ERROR"
cat "$outputs_file" 2>/dev/null || true
cd "$ROOT_DIR"
exit 1
fi
}
deploy_cloudformation_stack() {
local terraform_outputs_file="$1"
log_message "Deploying CloudFormation CI/CD pipeline:" "INFO"
local stack_name="devsecops-cloudformation"
local template_file="$CLOUDFORMATION_DIR/codepipeline.yaml"
if [[ ! -f "$template_file" ]]; then
log_message " CloudFormation template not found at: $template_file" "ERROR"
exit 1
fi
#- Build parameter overrides- #
local parameters=()
#- Terraform infrastructure outputs as CloudFormation parameters -#
if [[ -f "$terraform_outputs_file" ]]; then
log_message "Using Terraform outputs for infrastructure parameters:" "INFO"
local staging_ecs_cluster=$(jq -r '.staging_cluster_name.value // empty' "$terraform_outputs_file")
log_message " - Staging ECS Cluster: $staging_ecs_cluster" "DEBUG"
local staging_ecs_service=$(jq -r '.staging_service_name.value // empty' "$terraform_outputs_file")
log_message " - Staging ECS Service: $staging_ecs_service" "DEBUG"
local prod_ecs_cluster=$(jq -r '.production_cluster_name.value // empty' "$terraform_outputs_file")
log_message " - Production ECS Cluster: $prod_ecs_cluster" "DEBUG"
local prod_ecs_service=$(jq -r '.production_service_name.value // empty' "$terraform_outputs_file")
log_message " - Production ECS Service: $prod_ecs_service" "DEBUG"
local staging_auto_scaling_group=$(jq -r '.staging_auto_scaling_group_name.value // empty' "$terraform_outputs_file")
log_message " - Staging Auto Scaling Group: $staging_auto_scaling_group" "DEBUG"
local staging_ecs_task_definition=$(jq -r '.staging_task_definition_name.value // empty' "$terraform_outputs_file")
log_message " - Staging ECS Task Definition: $staging_ecs_task_definition" "DEBUG"
local prod_ecs_task_definition=$(jq -r '.production_task_definition_name.value // empty' "$terraform_outputs_file")
log_message " - Production ECS Task Definition: $prod_ecs_task_definition" "DEBUG"
local ecr_registry_name=$(jq -r '.ecr_repository_name.value // empty' "$terraform_outputs_file")
log_message " - ECR Repository Name: $ecr_registry_name" "DEBUG"
local artifact_bucket=$(jq -r '.artifact_bucket_name.value // empty' "$terraform_outputs_file")
log_message " - Artifact S3 Bucket: $artifact_bucket" "DEBUG"
local lambda_bucket=$(jq -r '.lambda_bucket_name.value // empty' "$terraform_outputs_file")
log_message " - Lambda S3 Bucket: $lambda_bucket" "DEBUG"
local lambda_s3_key=$(jq -r '.lambda_package_key.value // empty' "$terraform_outputs_file")
log_message " - Lambda S3 Key: $lambda_s3_key" "DEBUG"
local lambda_handler="lambda_handler.lambda_handler"
local vpc_id=$(jq -r '.vpc_id.value // empty' "$terraform_outputs_file")
log_message " - VPC ID: $vpc_id" "DEBUG"
local private_subnets=$(jq -r '.private_subnet_ids.value | join(",") // empty' "$terraform_outputs_file")
log_message " - Private Subnet IDs: $private_subnets" "DEBUG"
local codebuild_sg=$(jq -r '.codebuild_security_group_id.value // empty' "$terraform_outputs_file")
log_message " - CodeBuild Security Group ID: $codebuild_sg" "DEBUG"
local app_url_for_dast=$(jq -r '.staging_alb_dns_name.value // empty' "$terraform_outputs_file")
log_message " - App URL for DAST: $app_url_for_dast" "DEBUG"
parameters+=(
"StagingECSCluster=$staging_ecs_cluster"
"StagingECSService=$staging_ecs_service"
"ProdECSCluster=$prod_ecs_cluster"
"ProdECSService=$prod_ecs_service"
"StagingECSTaskDefinition=$staging_ecs_task_definition"
"ProdECSTaskDefinition=$prod_ecs_task_definition"
"StagingASGName=$staging_auto_scaling_group"
"EcrRegistryName=$ecr_registry_name"
"PipelineArtifactS3Bucket=$artifact_bucket"
"LambdaS3Bucket=$lambda_bucket"
"LambdaS3Key=$lambda_s3_key"
"LambdaHandler=$lambda_handler"
"VpcId=$vpc_id"
"PrivateSubnetIds=$private_subnets"
"CodeBuildSecurityGroupId=$codebuild_sg"
"AppURLForDAST=$app_url_for_dast"
)
else
#- Prompt user for existing custom infrastructure values -#
log_message "Provide your existing infrastructure details:" "WARNING"
echo
read -p "Staging ECS Cluster Name: " staging_ecs_cluster
read -p "Staging ECS Service Name: " staging_ecs_service
read -p "Production ECS Cluster Name: " prod_ecs_cluster
read -p "Production ECS Service Name: " prod_ecs_service
read -p "Staging Auto Scaling Group Name: " staging_auto_scaling_group
read -p "Staging ECS Task Definition Name: " staging_ecs_task_definition
read -p "Production ECS Task Definition Name: " prod_ecs_task_definition
read -p "ECR Repository Name: " ecr_registry_name
read -p "Pipeline Artifact S3 Bucket Name: " artifact_bucket
read -p "Lambda S3 Bucket Name: " lambda_bucket
read -p "Lambda S3 Key (example: lambda/lambda.zip): " lambda_s3_key
read -p "Lambda Handler (e.g., lambda_function.lambda_handler): " lambda_handler
read -p "App URL for DAST (Staging): " app_url_for_dast
read -p "VPC ID: " vpc_id
read -p "Private Subnet IDs (comma-separated): " private_subnets
read -p "CodeBuild Security Group ID: " codebuild_sg
parameters+=(
"StagingECSCluster=$staging_ecs_cluster"
"StagingECSService=$staging_ecs_service"
"ProdECSCluster=$prod_ecs_cluster"
"ProdECSService=$prod_ecs_service"
"StagingECSTaskDefinition=$staging_ecs_task_definition"
"ProdECSTaskDefinition=$prod_ecs_task_definition"
"StagingASGName=$staging_auto_scaling_group"
"EcrRegistryName=$ecr_registry_name"
"PipelineArtifactS3Bucket=$artifact_bucket"
"LambdaS3Bucket=$lambda_bucket"
"LambdaS3Key=$lambda_s3_key"
"LambdaHandler=$lambda_handler"
"VpcId=$vpc_id"
"PrivateSubnetIds=$private_subnets"
"CodeBuildSecurityGroupId=$codebuild_sg"
"AppURLForDAST=$app_url_for_dast"
)
fi
#- Environment configuration parameters -#
parameters+=(
"GitProviderType=$GIT_PROVIDER_TYPE"
"FullGitRepositoryId=$FULL_GIT_REPOSITORY_ID"
"BranchName=$BRANCH_NAME"
"SnykAPIKey=$SNYK_API_KEY"
"PipelineNotificationMail=$PIPELINE_NOTIFICATION_MAIL"
"PipelineManualApproverMail=$PIPELINE_MANUAL_APPROVER_MAIL"
"DockerHubUsername=$DOCKERHUB_USERNAME"
"DockerHubPassword=$DOCKERHUB_PASSWORD"
)
#- Deploy CloudFormation stack -#
log_message "Deploying CloudFormation stack: $stack_name" "INFO"
if aws cloudformation deploy \
--template-file "$template_file" \
--stack-name "$stack_name" \
--parameter-overrides "${parameters[@]}" \
--capabilities CAPABILITY_NAMED_IAM \
--region "$AWS_REGION" \
--no-fail-on-empty-changeset; then
log_message "CloudFormation stack deployed successfully" "SUCCESS"
#- Get stack outputs -#
log_message "Retrieving stack outputs:" "INFO"
aws cloudformation describe-stacks \
--stack-name "$stack_name" \
--query 'Stacks[0].Outputs' \
--region "$AWS_REGION" \
--output table
else
log_message " CloudFormation stack deployment failed!" "ERROR"
exit 1
fi
}
destroy_cloudformation_stack() {
local stack_name="devsecops-cloudformation"
log_message "Destroying CloudFormation stack: $stack_name" "WARNING"
if aws cloudformation delete-stack --stack-name "$stack_name" --region "$AWS_REGION"; then
log_message "CloudFormation stack deletion initiated" "INFO"
log_message "Waiting for stack to be deleted..." "INFO"
aws cloudformation wait stack-delete-complete --stack-name "$stack_name" --region "$AWS_REGION"
log_message "CloudFormation stack deleted successfully" "SUCCESS"
else
log_message "Failed to initiate CloudFormation stack deletion!" "ERROR"
exit 1
fi
}
print_next_steps() {
echo
log_message "AWS DevSecOps Hybrid CI/CD Platform deployment deployed successfully" "SUCCESS"
echo
log_message "Next steps:" "INFO"
echo " 1. Complete the CodeConnections connection in AWS Console"
echo " 2. Create an AWS Organization for AWS Security Hub [if not done already]"
echo " 3. Enable AWS Security Hub CSPM (Services + Policies) in your account [if not done already]"
echo " 4. Verify SNS email subscriptions in your inbox"
echo " 5. Monitor pipeline execution in AWS CodePipeline console"
echo
}
main() {
echo -e "${BLUE}╔══════════════════════════════════════════════════════════════════════════════╗${NOCOLOR}"
echo -e "${BLUE}║ AWS DevSecOps Hybrid CI/CD Platform ║${NOCOLOR}"
echo -e "${BLUE}║ Deployment Script (bash) v2.0 ║${NOCOLOR}"
echo -e "${BLUE}║ ║${NOCOLOR}"
echo -e "${BLUE}║${NOCOLOR} Author: Haitam Bidiouane (@sch0penheimer) ${BLUE}║${NOCOLOR}"
echo -e "${BLUE}╚══════════════════════════════════════════════════════════════════════════════╝${NOCOLOR}"
echo
if [[ "$SHOW_HELP" == true ]]; then
show_help
exit 0
fi
log_message "Starting AWS DevSecOps Hybrid CI/CD Platform deployment:" "SUCCESS"
log_message "OS detected: $(detect_os)" "INFO"
check_prerequisites
load_environment
configure_aws_credentials
#- Handle rollback operation -#
if [[ "$ROLLBACK_DEPLOYMENT" == true ]]; then
destroy_infrastructure
log_message "Infrastructure destruction completed." "SUCCESS"
destroy_cloudformation_stack
log_message "CloudFormation stack destruction completed." "SUCCESS"
log_message "Deployment Rollbacked Successfully !" "SUCCESS"
exit 0
fi
#- I. Create Lambda ZIP package -#
create_lambda_package
#- II. Deploy platform infrastructure (or skip) -#
local terraform_outputs_file=""
if [[ "$SKIP_INFRASTRUCTURE" == false ]]; then
log_message "Proceeding with platform's Terraform infrastructure deployment:" "INFO"
deploy_infrastructure
get_terraform_outputs
terraform_outputs_file="/tmp/terraform_outputs.json"
else
log_message "Platform Infrastructure deployment skipped as requested." "WARNING"
fi
#- III. Deploy CloudFormation pipeline stack -#
deploy_cloudformation_stack "$terraform_outputs_file"
#- IV. Print guiding next steps -#
print_next_steps
}
#- Trap errors and cleanup -#
trap 'log_message "Script interrupted or failed" "ERROR"; exit 1' ERR INT TERM
#- Execute main function -#
main "$@"