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
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725#! /usr/bin/env bash
# Setting up color and style variables
bold=$(tput bold)
blue=$(tput setaf 4)
green=$(tput setaf 2)
yellow=$(tput setaf 3)
red=$(tput setaf 1)
reset=$(tput sgr0)
source ./bash/utils.sh
function echoLog() {
echo "$1" | tee -a $LOG_FILE
}
# Function to display error messages in red
display_error() {
echo "${bold}${red}$1${reset}"
}
if [[ -z "$AWS_ACCESS_KEY_ID" || -z "$AWS_SECRET_ACCESS_KEY" ]]; then
display_error "Missing AWS credentials. Please configure the AWS CLI with your credentials."
exit 1
fi
if [[ -z "$AWS_SESSION_TOKEN" ]]; then
echo "${bold}${yellow}Missing AWS session token. If you are using 2FA, please export the temporary session token as the AWS_SESSION_TOKEN environment variable."
echo "Proceeding anyway...${reset}"
sleep 2
fi
function box_out() {
local s=("$@") b w padding terminalWidth
for l in "${s[@]}"; do
((w < ${#l})) && {
b="$l"
w="${#l}"
}
done
terminalWidth=$(tput cols) # Get the terminal width
padding=$(((terminalWidth - w - 4) / 2)) # Calculate padding; subtract 4 for the box borders
tput bold
tput setaf 2
printf "%*s" $padding "" # Add padding before the top line
echo " ${b//?/ } "
for l in "${s[@]}"; do
printf "%*s" $padding "" # Add padding before each line within the box
printf " %s%*s%s " "$(tput sgr 0)$(tput bold)" "-$w" "$l" "$(tput bold)$(tput setaf 2)"
echo # New line
done
tput sgr 0
}
function box_out_with_hyphen() {
local s=("$@") b w padding terminalWidth
for l in "${s[@]}"; do
((w < ${#l})) && {
b="$l"
w="${#l}"
}
done
terminalWidth=$(tput cols) # Get the terminal width
padding=$(((terminalWidth - w - 4) / 2)) # Calculate padding; subtract 4 for the box borders
tput bold
tput setaf 2
printf "%*s" $padding "" # Add padding before the top line
echo " -${b//?/-}-"
printf "%*s" $padding "" # Add padding before the second line
echo "| ${b//?/ } |"
for l in "${s[@]}"; do
printf "%*s" $padding "" # Add padding before each line within the box
printf "| %s%*s%s |" "$(tput sgr 0)$(tput bold)" "-$w" "$l" "$(tput bold)$(tput setaf 2)"
echo # New line
done
printf "%*s" $padding "" # Add padding before the bottom second line
echo "| ${b//?/ } |"
printf "%*s" $padding "" # Add padding before the bottom line
echo " -${b//?/-}-"
tput sgr 0
}
bash ./bash/deps.sh
echo "Dependency installation completed."
fetch_details() {
# Trying to retrieve AWS account owner's details
if ! AWS_ACCOUNT_DETAILS_JSON=$(aws sts get-caller-identity 2>&1); then
display_error "Unable to obtain AWS caller identity: $AWS_ACCOUNT_DETAILS_JSON"
display_error "Check if your AWS credentials are expired and you have appropriate permissions."
exit 1
fi
# Extracting and displaying account details
AWS_ACCOUNT_ID=$(echo "$AWS_ACCOUNT_DETAILS_JSON" | jq -r '.Account')
AWS_USER_ID=$(echo "$AWS_ACCOUNT_DETAILS_JSON" | jq -r '.UserId')
AWS_ARN=$(echo "$AWS_ACCOUNT_DETAILS_JSON" | jq -r '.Arn')
AWS_ROLE=$(aws sts get-caller-identity --query 'Arn' --output text | cut -d '/' -f 2)
}
show_loader "Fetching AWS account details" &
fetch_details
# Waiting for the fetch_details background process to complete
wait
# Check if fetch_details exited with an error
if [ $? -ne 0 ]; then
echo "Error fetching AWS details. Exiting script."
exit 1
fi
# Displaying AWS account information in a "box"
echo
box_out_with_hyphen "AWS Account Information:" "" "Account ID: $AWS_ACCOUNT_ID" "User ID: $AWS_USER_ID" "Role: $AWS_ROLE"
echo
# Ask consent to proceed with the aws account
while true; do
read -r -p "Do you want to proceed with the above AWS account? [y/n]: " yn
case $yn in
[Yy]*)
echo "Proceeding with AWS account $AWS_ACCOUNT_ID"
break
;;
[Nn]*)
echo "Exiting..."
exit
;;
*) echo "Please answer yes or no [y/n]." ;;
esac
done
# Function to display the header
echo "Checking dependencies..."
# Check for Node.js
if ! command -v node &>/dev/null; then
echo "Node.js could not be found. Please install node js 18 or above."
exit 1
fi
# Verify Node.js version
version=$(node -v | cut -d'.' -f1 | tr -d 'v')
if [ "$version" -lt 18 ]; then
echo "Invalid Node.js version. Expected 18 or above, but got $version."
exit 1
fi
echo "Node.js version is valid."
# Function to list available services
list_services() {
box_out_with_hyphen " Welcome to Hyperswitch Services Installer" "" "" "Hyperswitch Services Available for Installation:" " 1. Backend Services" " 2. Demo Store" " 3. Control Center" " 4. Card Vault" " 5. SDK"
}
INSTALLATION_MODE=1
# Function to show installation options
show_install_options() {
echo
echo "${bold}Choose an installation option:${reset}"
echo "${bold}${green}1. Free Tier ${reset} - ${bold}${blue}Only for prototyping and not scalable. Falls under AWS Free Tier. ${reset}"
echo "${bold}${green}2. Production Ready ${reset} - ${bold}${blue}Optimized for scalability and performance, leveraging the power of AWS EKS for robust, enterprise-grade deployments.${reset}"
}
declare base_ami,envoy_ami,squid_ami
check_image_builder() {
ssm=$(aws ssm get-parameters \
--names base_image_ami squid_image_ami envoy_image_ami \
--query "Parameters[*].{Name:Name,Value:Value}" --output json)
length=$(echo "$ssm" | jq -r ".|length")
if [ "$length" -lt 3 ]; then
display_error "Unable to find base images for Proxy Servers. Please run the following command: bash deploy_imagebuilder.sh\nIf you have done it already please wait for 15-20 mins until it builds the images"
exit 1
fi
base_ami=$(echo "$ssm" | jq -r '.[]|select(.Name=="base_image_ami")|.Value')
envoy_ami=$(echo "$ssm" | jq -r '.[]|select(.Name=="envoy_image_ami")|.Value')
squid_ami=$(echo "$ssm" | jq -r '.[]|select(.Name=="squid_image_ami")|.Value')
}
# Function to read user input until a valid choice is made
get_user_choice() {
while true; do
read -r -p "Enter your choice [1-2]: " INSTALLATION_MODE
case $INSTALLATION_MODE in
1)
echo "Free Tier option selected."
break
;;
2)
echo "Production Ready option selected."
break
;;
*) echo "Invalid choice. Please enter 1 or 2." ;;
esac
done
}
clear
list_services
echo
show_install_options
get_user_choice
check_if_element_is_preset_in_array() {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
if [[ -z "$AWS_DEFAULT_REGION" ]]; then
read -p "Please enter the AWS region to deploy the services: " AWS_DEFAULT_REGION
else
read -p "Please enter the AWS region to deploy the services (Press enter to keep the current region $blue$bold$AWS_DEFAULT_REGION$reset): " input_region
if [[ -n "$input_region" ]]; then
AWS_DEFAULT_REGION=$input_region
fi
fi
export AWS_DEFAULT_REGION;
# Prompt for region and check if it's enabled
while true; do
AVAILABLE_REGIONS_JSON=$(aws ec2 describe-regions --query 'Regions[].RegionName' --output text 2>&1)
if [[ $AVAILABLE_REGIONS_JSON == *"UnauthorizedOperation"* ]]; then
display_error "Error: Unauthorized operation. You do not have permission to perform 'ec2:DescribeRegions'."
display_error "Contact your AWS administrator to obtain the necessary permissions."
exit 1
elif [[ $AVAILABLE_REGIONS_JSON == *"supported format"* ]]; then
display_error "Error: Invalid region format. Please enter a valid region code (e.g. us-east-1)."
else
# Convert the region list into an array
AVAILABLE_REGIONS=($AVAILABLE_REGIONS_JSON)
# Check if AWS_DEFAULT_REGION is in the list of available regions
if [[ " ${AVAILABLE_REGIONS[*]} " =~ " $AWS_DEFAULT_REGION " ]]; then
echo "Region $AWS_DEFAULT_REGION is enabled for your account."
break
else
display_error "Error: Region $AWS_DEFAULT_REGION is not enabled for your account or invalid region code."
fi
fi
# Prompt for region again
read -p "Please enter the AWS region to deploy the services: " AWS_DEFAULT_REGION
done
echo
printf "${bold}Checking neccessary permissions${reset}\n"
echo
check_root_user() {
AWS_ARN=$(aws sts get-caller-identity --output json | jq -r .Arn)
if [[ $AWS_ARN == *":root"* ]]; then
echo "ROOT user is not recommended. Please create a new user with AdministratorAccess and use their Access Token."
exit 1
fi
}
REQUIRED_POLICIES=("AdministratorAccess") # Add other necessary policies to this array
# Check if the current user is a root user
echo "Verifying that you're not using the AWS root account..."
echo "(For security reasons, it's best to avoid using the root account.)"
(check_root_user) &
show_loader "Verifying root user status"
check_iam_policies() {
USER_POLICIES=$(aws iam list-attached-role-policies --role-name "$AWS_ROLE" --output json | jq -r '.AttachedPolicies[].PolicyName')
for policy in "${REQUIRED_POLICIES[@]}"; do
if ! echo "$USER_POLICIES" | grep -q "$policy"; then
echo "Required policy $policy is not attached to your user. Please attach this policy."
exit 1
fi
done
echo "All necessary permissions are in place."
}
# Check for specific IAM policies
echo "Checking for necessary IAM policies..."
(check_iam_policies) &
show_loader "Verifying IAM policies"
echo
printf "${bold}Configure Credentials of the Application${reset}\n"
echo
validate_password() {
local password=$1
# Check length (at least 8 characters)
if [[ ${#password} -lt 8 ]]; then
display_error "Error: Password must be at least 8 characters."
return 1
fi
# Check if it starts with an alphabet
if [[ ! $password =~ ^[A-Za-z] ]]; then
display_error "Error: Password must start with a letter."
return 1
fi
# Check for at least one uppercase letter and one lowercase letter
if [[ ! $password =~ [A-Z] || ! $password =~ [a-z] ]]; then
display_error "Error: Password must include at least one uppercase and one lowercase letter."
return 1
fi
# Check for at least one digit
if [[ ! $password =~ [0-9] ]]; then
display_error "Error: Password must include at least one digit."
return 1
fi
# Check for forbidden special characters
if [[ $password =~ [^A-Za-z0-9] ]]; then
display_error "Error: Password cannot include special characters."
return 1
fi
# read password again to confirm
echo "Please re-enter the password: "
read -r -s password_confirm
if [[ "$password" != "$password_confirm" ]]; then
display_error "Error: Passwords do not match."
return 1
fi
return 0
}
# Prompt for DB Password
while true; do
echo "Please enter the password for your RDS instance (Minimum 8 characters; includes [A-Z], [a-z], [0-9]): "
read -r -s DB_PASS
if validate_password "$DB_PASS"; then
break
fi
done
validate_api_key() {
local api_key=$1
if [[ ! $api_key =~ ^[A-Za-z0-9_]{8,}$ ]]; then
display_error "Error: API Key must be at least 8 characters long and can include letters, numbers, and underscores."
return 1
fi
# read api_key again to confirm
echo "Please re-enter the api-key: "
read -r -s api_key_confirm
if [[ "$api_key" != "$api_key_confirm" ]]; then
display_error "Error: Api Keys do not match."
return 1
fi
return 0
}
validate_access_token() {
local token=$1
# Check minimum length
if [[ ${#token} -lt 32 ]]; then
display_error "Error: Access token must be at least 32 characters long."
return 1
fi
# Check for valid characters
if [[ ! $token =~ ^[A-Za-z0-9_+/=-]+$ ]]; then
display_error "Error: Access token contains invalid characters."
return 1
fi
return 0
}
validate_hash_context() {
local context=$1
# Check if it follows the pattern "service:domain"
if [[ ! $context =~ ^[a-zA-Z0-9_-]+:[a-zA-Z0-9_-]+$ ]]; then
display_error "Error: Hash context should follow the pattern 'service:domain' (e.g., 'keymanager:hyperswitch')."
return 1
fi
return 0
}
# Prompt for Admin API Key
while true; do
echo "Please enter the Admin API key (required to access Hyperswitch APIs): "
read -r -s ADMIN_API_KEY
if validate_api_key "$ADMIN_API_KEY"; then
break
fi
done
validate_opensearch_password() {
local master_password=$1
# Check length (at least 8 characters)
if [[ ${#master_password} -lt 8 ]]; then
display_error "Error: Password must be at least 8 characters."
return 1
fi
# Check if it starts with an alphabet
if [[ ! $master_password =~ ^[A-Za-z] ]]; then
display_error "Error: Password must start with a letter."
return 1
fi
# Check for at least one uppercase letter and one lowercase letter
if [[ ! $master_password =~ [A-Z] || ! $master_password =~ [a-z] ]]; then
display_error "Error: Password must include at least one uppercase and one lowercase letter."
return 1
fi
# Check for at least one digit
if [[ ! $master_password =~ [0-9] ]]; then
display_error "Error: Password must include at least one digit."
return 1
fi
# Check for special characters
if [[ $password == [^A-Za-z0-9] ]]; then
display_error "Error: Password should include special characters."
return 1
fi
# read password again to confirm
echo "Please re-enter the password: "
read -r -s master_password_confirm
if [[ "$master_password" != "$master_password_confirm" ]]; then
display_error "Error: Passwords do not match."
return 1
fi
return 0
}
generate_keymanager_certificates() {
rm ca_cert.pem ca_key.pem client.pem rsa_sha256_cert.pem rsa_sha256_key.pem 2> /dev/null
echo "${bold}${green}Generating certificate for keymanager ...${reset}"
curl -s https://raw.githubusercontent.com/juspay/hyperswitch-encryption-service/main/scripts/tls/gen_certs.sh --output gen_certs.sh
bash gen_certs.sh --prod --namespace keymanager --service keymanager > /dev/null 2>&1
rm gen_certs.sh
echo "${bold}${green}Generating certificate done${reset}"
}
# Commented out the Open Search Service feature, will be added in future releases
# echo "Do you want to push logs to S3 and Open Search? [y/n]: "
# while true; do
# read -r OPEN_SEARCH_SERVICE
# if [[ "$OPEN_SEARCH_SERVICE" == "y" ]]; then
# read -p "Please enter the Master UserName for Open Search Service: " OPEN_SEARCH_MASTER_USER_NAME
# while true; do
# echo "Please enter the Master Password for Open Search Service: "
# read -r -s OPEN_SEARCH_MASTER_PASSWORD
# if validate_opensearch_password "$OPEN_SEARCH_MASTER_PASSWORD"; then
# break
# fi
# done
# break
# elif [[ "$OPEN_SEARCH_SERVICE" == "n" ]]; then
# echo "Logs will not be pushed to S3 and Open Search."
# break
# else
# echo "Invalid input. Please enter 'y' or 'n'."
# fi
# done
if [[ "$INSTALLATION_MODE" == 2 ]]; then
while true; do
echo "Please enter the AES master encryption key. It must be 64 characters long and consist of hexadecimal digits:"
echo "${bold}${red}Please create the AES master encryption key as described below.${reset}"
echo "${bold}${yellow}To generate the master key, run the following command:${reset}${bold}${green} openssl enc -aes-256-cbc -k secret -P -md sha1${reset}"
echo "${bold}${yellow}Copy the value of 'key' from the output and use it as the master key.${reset}"
read -r -s MASTER_ENC_KEY
if [[ ${#MASTER_ENC_KEY} -eq 64 && $MASTER_ENC_KEY =~ ^[0-9a-fA-F]+$ ]]; then
break
else
display_error "Invalid input. The master encryption key must be 64 characters long and consist of hexadecimal digits."
fi
done
echo "Please enter the IP addresses that you want to whitelist for the EKS cluster. If you have multiple IP addresses, separate them with commas."
echo "If you do not have static ip you can enter${bold}${red} 0.0.0.0, but this will make the cluster open to internet ${reset}."
read -r VPN_IPS
echo
echo "Do you want to deploy the Card Vault? [y/n]: "
read -r CARD_VAULT
LOCKER=""
if [[ "$CARD_VAULT" == "y" ]]; then
# Instructions for Card Vault Master Key
echo "${bold}${red}If you require the Card Vault, create a master key as described below.${reset}"
echo "${bold}${yellow}To generate the master key, use the utility at: https://github.com/juspay/hyperswitch-card-vault${reset}"
echo "${bold}${yellow}With cargo installed, run: cargo install --git https://github.com/juspay/hyperswitch-card-vault --bin utils --root . && ./bin/utils master-key && rm ./bin/utils && rmdir ./bin${reset}"
# Prompt for Encrypted Master Key
echo "Enter your encrypted master key:"
read -r -s MASTER_KEY
LOCKER+="-c master_key=$MASTER_KEY "
# Prompt for Locker DB Password
while true; do
echo "Please enter the password for your RDS instance (Minimum 8 characters; includes [A-Z], [a-z], [0-9]): "
read -r -s LOCKER_DB_PASS
if validate_password "$LOCKER_DB_PASS"; then
break
fi
done
LOCKER+="-c locker_pass=$LOCKER_DB_PASS "
fi
echo
echo "Do you want to deploy Keymanager? (Mandatory for non standalone builds) [y/n]: "
read -r KEYMANAGER
KEYMANAGER_ENABLED=""
if [[ "$KEYMANAGER" == "y" ]]; then
generate_keymanager_certificates
# Prompt for Access Token
echo "${bold}${blue}Keymanager Access Token Configuration${reset}"
echo "The access token is a cryptographically secure string used for authenticating requests to the keymanager service."
echo "${bold}${yellow}To generate a secure access token, you can use:${reset}${bold}${green} openssl rand -hex 32${reset}"
while true; do
echo "Please enter the Keymanager Access Token (minimum 32 characters):"
read -r -s KEYMANAGER_ACCESS_TOKEN
if validate_access_token "$KEYMANAGER_ACCESS_TOKEN"; then
break
fi
done
# Prompt for Hash Context
echo "${bold}${blue}Keymanager Hash Context Configuration${reset}"
echo "The hash context is a domain separation string "
while true; do
echo "Please enter the Hash Context (or press Enter for default 'keymanager:hyperswitch'):"
read -r KEYMANAGER_HASH_CONTEXT
if [[ -z "$KEYMANAGER_HASH_CONTEXT" ]]; then
KEYMANAGER_HASH_CONTEXT="keymanager:hyperswitch"
echo "Using default: keymanager:hyperswitch"
break
elif validate_hash_context "$KEYMANAGER_HASH_CONTEXT"; then
break
fi
done
KEYMANAGER_ENABLED+="-c keymanager_enabled=true "
KEYMANAGER_ENABLED+="-c keymanager_access_token=$KEYMANAGER_ACCESS_TOKEN "
KEYMANAGER_ENABLED+="-c keymanager_hash_context=$KEYMANAGER_HASH_CONTEXT"
fi
echo
echo "Do you want to deploy proxy setup (Envoy/Squid)? [y/n]: "
read -r APP_PROXY_SETUP
APP_PROXY_CONTEXT=""
if [[ "$APP_PROXY_SETUP" == "y" ]]; then
echo "Checking for required AMI images for proxy setup..."
check_image_builder
APP_PROXY_CONTEXT="-c app_proxy_enabled=true -c envoy_ami=$envoy_ami -c squid_ami=$squid_ami"
fi
echo
printf "${bold}Deploying Hyperswitch Services${reset}\n"
# Deploy the EKS Cluster
npm install
export JSII_SILENCE_WARNING_UNTESTED_NODE_VERSION=true
echo "Bootstrapping CDK environment..."
if ! cdk bootstrap aws://$AWS_ACCOUNT_ID/$AWS_DEFAULT_REGION -c aws_arn=$AWS_ARN; then
BUCKET_NAME=cdk-hnb659fds-assets-$AWS_ACCOUNT_ID-$AWS_DEFAULT_REGION
ROLE_NAME=cdk-hnb659fds-cfn-exec-role-$AWS_ACCOUNT_ID-$AWS_DEFAULT_REGION
aws s3api delete-objects --bucket $BUCKET_NAME --delete "$(aws s3api list-object-versions --bucket $BUCKET_NAME --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}')" 2>/dev/null
aws s3api delete-objects --bucket $BUCKET_NAME --delete "$(aws s3api list-object-versions --bucket $BUCKET_NAME --query='{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}}')" 2>/dev/null
aws s3 rm s3://$BUCKET_NAME --recursive 2>/dev/null
aws s3api delete-bucket --bucket $BUCKET_NAME 2>/dev/null
for policy_arn in $(aws iam list-attached-role-policies --role-name $ROLE_NAME --query 'AttachedPolicies[].PolicyArn' --output text); do
aws iam detach-role-policy --role-name $ROLE_NAME --policy-arn $policy_arn 2>/dev/null
done
aws iam delete-role --role-name $ROLE_NAME 2>/dev/null
cdk bootstrap aws://$AWS_ACCOUNT_ID/$AWS_DEFAULT_REGION -c aws_arn=$AWS_ARN
fi
echo
echo "${bold}Deploying Hyperswitch Stack...${reset}"
echo
# Single deployment that includes everything based on user choices
if cdk deploy --require-approval never -c db_pass=$DB_PASS -c admin_api_key=$ADMIN_API_KEY -c aws_arn=$AWS_ARN -c master_enc_key=$MASTER_ENC_KEY -c vpn_ips=$VPN_IPS -c base_ami=$base_ami $LOCKER $KEYMANAGER_ENABLED $APP_PROXY_CONTEXT -c open_search_service=$OPEN_SEARCH_SERVICE -c open_search_master_user_name=$OPEN_SEARCH_MASTER_USER_NAME -c open_search_master_password=$OPEN_SEARCH_MASTER_PASSWORD; then
echo "Stack deployment successful."
echo "Creating CloudWatch Observability addon..."
aws eks create-addon --cluster-name hs-eks-cluster --addon-name amazon-cloudwatch-observability --resolve-conflicts PRESERVE > /dev/null 2>&1 || echo "Failed to create/update cloudwatch-observability addon. It might already exist or an error occurred."
echo "Updating kubeconfig..."
aws eks update-kubeconfig --region "$AWS_DEFAULT_REGION" --name hs-eks-cluster
echo "Please wait for the EKS cluster to be initialized. Approximate time is 2 minutes..."
sleep 120
# Retrieve ingress hostnames for CloudFront configuration
echo "Retrieving ingress hostnames..."
CONTROL_CENTER_HOST=$(kubectl get ingress hyperswitch-control-center-ingress -n hyperswitch -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null)
# Conditionally get APP_HOST based on whether proxy is enabled
if [[ "$APP_PROXY_SETUP" == "y" ]]; then
EXT_ALB_DNS=$(aws elbv2 describe-load-balancers --names external-lb --query 'LoadBalancers[0].DNSName' --output text)
if [ -n "$EXT_ALB_DNS" ] && [ "$EXT_ALB_DNS" != "null" ]; then
APP_HOST="$EXT_ALB_DNS"
echo "Using External ALB as App Host: $APP_HOST"
else
echo "External ALB DNS not found."
APP_HOST=$(kubectl get ingress hyperswitch-alb-ingress -n hyperswitch -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null)
echo "Using ALB ingress as App Host: $APP_HOST"
fi
else
# Default approach for non-proxy setup
APP_HOST=$(kubectl get ingress hyperswitch-alb-ingress -n hyperswitch -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null)
echo "Using ALB ingress as App Host: $APP_HOST"
fi
if [ -n "$CONTROL_CENTER_HOST" ] && [ -n "$APP_HOST" ]; then
echo "Control Center Host: $CONTROL_CENTER_HOST"
# Update CloudFront distributions with the ingress hostnames
echo "Updating CloudFront distributions..."
cdk deploy --require-approval never \
-c db_pass=$DB_PASS -c admin_api_key=$ADMIN_API_KEY -c aws_arn=$AWS_ARN \
-c master_enc_key=$MASTER_ENC_KEY -c vpn_ips=$VPN_IPS -c base_ami=$base_ami \
$LOCKER $KEYMANAGER_ENABLED $APP_PROXY_CONTEXT \
-c open_search_service=$OPEN_SEARCH_SERVICE \
-c open_search_master_user_name=$OPEN_SEARCH_MASTER_USER_NAME \
-c open_search_master_password=$OPEN_SEARCH_MASTER_PASSWORD \
-c control_center_host="$CONTROL_CENTER_HOST" \
-c app_host="$APP_HOST"
if [ $? -ne 0 ]; then
echo "${bold}${red}Error updating CloudFront distributions.${reset}"
else
echo "CloudFront distributions updated successfully."
fi
else
echo "${bold}${yellow}Warning: Could not retrieve ingress hostnames. CloudFront configuration may be incomplete.${reset}"
fi
echo "Finalizing setup..."
helm get values -n hyperswitch hypers-v1 > values.yaml 2>/dev/null || echo "Failed to get helm values for hypers-v1"
sh upgrade.sh "$ADMIN_API_KEY" "$CARD_VAULT" "$APP_PROXY_SETUP" "$KEYMANAGER"
echo "โ
All deployments complete!"
exit 0
fi
else
echo
printf "${bold}Deploying Hyperswitch Services${reset}\n"
echo
echo "Hyperswitch is being deployed in standalone mode. Please wait for the deployment to complete."
npm install
if ! cdk bootstrap aws://$AWS_ACCOUNT_ID/$AWS_DEFAULT_REGION -c aws_arn=$AWS_ARN; then
BUCKET_NAME=cdk-hnb659fds-assets-$AWS_ACCOUNT_ID-$AWS_DEFAULT_REGION
ROLE_NAME=cdk-hnb659fds-cfn-exec-role-$AWS_ACCOUNT_ID-$AWS_DEFAULT_REGION
aws s3api delete-objects --bucket $BUCKET_NAME --delete "$(aws s3api list-object-versions --bucket $BUCKET_NAME --query='{Objects: Versions[].{Key:Key,VersionId:VersionId}}')" 2>/dev/null
aws s3api delete-objects --bucket $BUCKET_NAME --delete "$(aws s3api list-object-versions --bucket $BUCKET_NAME --query='{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}}')" 2>/dev/null
aws s3 rm s3://$BUCKET_NAME --recursive 2>/dev/null
aws s3api delete-bucket --bucket $BUCKET_NAME 2>/dev/null
for policy_arn in $(aws iam list-attached-role-policies --role-name $ROLE_NAME --query 'AttachedPolicies[].PolicyArn' --output text); do
aws iam detach-role-policy --role-name $ROLE_NAME --policy-arn $policy_arn 2>/dev/null
done
aws iam delete-role --role-name $ROLE_NAME 2>/dev/null
cdk bootstrap aws://$AWS_ACCOUNT_ID/$AWS_DEFAULT_REGION -c aws_arn=$AWS_ARN
fi
if cdk deploy --require-approval never -c aws_arn=$AWS_ARN -c free_tier=true -c db_pass=$DB_PASS -c admin_api_key=$ADMIN_API_KEY; then
STANDALONE_HOST=$(aws cloudformation describe-stacks --stack-name hyperswitch --query "Stacks[0].Outputs[?OutputKey=='StandaloneURL'].OutputValue" --output text)
CONTROL_CENTER_HOST=$(aws cloudformation describe-stacks --stack-name hyperswitch --query "Stacks[0].Outputs[?OutputKey=='ControlCenterURL'].OutputValue" --output text)
SDK_HOST=$(aws cloudformation describe-stacks --stack-name hyperswitch --query "Stacks[0].Outputs[?OutputKey=='SdkAssetsURL'].OutputValue" --output text)
DEMO_APP=$(aws cloudformation describe-stacks --stack-name hyperswitch --query "Stacks[0].Outputs[?OutputKey=='DemoApp'].OutputValue" --output text)
echo "Please wait for instances to be initialized. Approximate time is 2 minutes."
printf "${bold}Initializing Instances${reset} "
# # Start the spinner in the background
# (
# while :; do
# for s in '/' '-' '\\' '|'; do
# printf "\r$s"
# sleep 1
# done
# done
# ) &
# spinner_pid=$!
# # Sleep for x seconds to simulate work
sleep 60
# Kill the spinner
# kill $spinner_pid >/dev/null 2>&1
# wait $spinner_pid 2>/dev/null # Ensures the spinner process is properly terminated before moving on
# printf "\r" # Clear the spinner character
printf "\nInitialization complete.\n"
printf "\n"
echoLog "--------------------------------------------------------------------------------"
echoLog "$bold Service Host$reset"
echoLog "--------------------------------------------------------------------------------"
echoLog "$green Standalone Hosted at $blue"$STANDALONE_HOST"$reset"
echoLog "$green Control center server running on $blue"$CONTROL_CENTER_HOST"$reset"
echoLog "$green SDK Hosted at $blue"$SDK_HOST"$reset"
#echoLog "$green Hyperswitch Demo Store running on $blue"$DEMO_APP"$reset"
echoLog "--------------------------------------------------------------------------------"
fi
fi