#!/bin/bash ############################################################ # # Copy Amazon Connect instance A to instance B safely # VERSION=1.3.5 SCRIPT_VERSION="$(basename $0) $VERSION" AWS_CLI_MAX_QUEUE_CONFIGS=10 USAGE=$(cat <&2; exit 2; } version() { echo -e "$SCRIPT_VERSION"; exit; } dos2unix() { tr -d '\r'; } error() { if [[ ! "$1" =~ ^[[:digit:]]+$ ]]; then # Not an AWS CLI error cat <&2 Error: $* EOD else # An AWS CLI error line_no=$1 shift cat <&2 $* Error at line ${line_no}. Recommended actions: Make sure all required prompts exist in the target instance, and Install the latest AWS CLI: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html . EOD fi exit 1 } hex_cmd= if [ -x "$(command -v xxd)" ]; then hex_cmd="xxd -u -p -c1" elif [ -x "$(command -v hexdump)" ]; then hex_cmd="hexdump -v -e '/1 \"%02X\n\"'" elif [ -x "$(command -v od)" ]; then hex_cmd="od -An -vtx1 | tr [:lower:] [:upper:] | for i in \$(cat); do echo \$i; done" fi test -z "$hex_cmd" && error "Cannot find any hex conversion commands. Please install one of these: xxd, hexdump, od" hex_code() { printf '%s' "$1" | eval "$hex_cmd" | while read x; do printf "%%%s" "$x"; done } path_encode() { old_lc_collate=$LC_COLLATE LC_COLLATE=C local length="${#1}" for (( i = 0; i < length; i++ )); do local c="${1:$i:1}" case $c in [a-zA-Z0-9.~_-]) printf '%s' "$c";; *) x=$(hex_code "$c"); echo -n ${x//%0D/};; esac done LC_COLLATE=$old_lc_collate } path_decode() { local path_encoded="${1//+/ }" printf '%b' "${path_encoded//%/\\x}" } diff_files() { # Compare TEMPA (sedded into TEMP1 and sorted into TEMP2) and TEMPB (sorted inline). # If same, return 0. If different, show the difference unsorted, # then ask if want to update. Return 1 if needs update and 0 otherwise. sed -f "$helper_sed" $TEMPA | tee $TEMP1 | sort > $TEMP2 sort $TEMPB | diff -qbB - $TEMP2 &> /dev/null if [ $? -eq 0 ]; then echo "same" else # sdiff $TEMPB $TEMP1 echo "changed" fi } sub_lex_bot() { if [ -z "$lex_bot_prefix_a" -a -z "$lex_bot_prefix_b" ]; then cat else cat > $TEMP1 local lexExists=$(cat $TEMP1 | jq ".Actions[] | select(.Type == \"ConnectParticipantWithLexBot\") | has(\"Type\")") if [ -z "$lexExists" ]; then cat $TEMP1 else cat $TEMP1 | jq " .Actions[] | if .Type == \"ConnectParticipantWithLexBot\" then .Parameters.LexBot.Name = (.Parameters.LexBot.Name | sub(\"^$lex_bot_prefix_a\"; \"$lex_bot_prefix_b\")) else . end " > $TEMP2 cat $TEMP1 | jq -r --slurpfile actions $TEMP2 ".Actions = \$actions" fi fi } check_contact_flow() { cf_json=$1 if [ ! -s $TEMPSED ]; then cat $helper_sed | egrep -v "^#" | cut -d% -f3 > $TEMPSED fi echo "### Broken references found in $cf_json:" > $TEMPCHK cat $cf_json | jq -r . | grep arn:aws:connect: | cut -d\" -f2,4 | sed -e's/"/ /g' | egrep -v "^id |^arn " | while read name val; do val_id=${val/arn:aws:connect:*\//} grep -q $val_id $TEMPSED || echo "### $name: $val_id" >> $TEMPCHK done if [ $(cat $TEMPCHK | wc -l) -le 1 ]; then > $TEMPCHK else echo >> $TEMPCHK cat $TEMPCHK return 1 fi } # AWS CLI needs to output JSON export AWS_DEFAULT_OUTPUT=json aws_connect() { local cmd="" local ii for ii; do [[ "$ii" =~ " " || "$ii" =~ "(" || "$ii" =~ ")" ]] && cmd="$cmd \"$ii\"" || cmd="$cmd $ii" done echo "aws connect$profile_flag$cmd" >> "$aws_cli_log" eval "aws connect$profile_flag$cmd" 2> $TEMPERR local ret=$? if [ -s $TEMPERR ]; then cat $TEMPERR | tee -a "$aws_cli_log" >&2 fi return $ret } TEMPFILE=$(mktemp) TEMPERR=${TEMPFILE}_err TEMPNEW=${TEMPFILE}_new TEMPOLD=${TEMPFILE}_old TEMPMOD=${TEMPFILE}_mod TEMPCHK=${TEMPFILE}_chk TEMPSED=${TEMPFILE}_sed TEMPA=${TEMPFILE}_A TEMPB=${TEMPFILE}_B TEMP1=${TEMPFILE}_1 TEMP2=${TEMPFILE}_2 TEMPLIST="$TEMPFILE $TEMPERR $TEMPNEW $TEMPOLD $TEMPMOD $TEMPCHK $TEMPSED $TEMPA $TEMPB $TEMP1 $TEMP2" trap 'rm -r -- $TEMPLIST' EXIT touch $TEMPLIST actionLead="# Action -" dryrun= ignore_improper_extended_ascii= while getopts "?dev" arg; do case "$arg" in d) dryrun=on; echo "Dry Run";; e) ignore_improper_extended_ascii=on;; v) version;; *) usage;; esac done shift $((OPTIND-1)) if [ "$(hex_code "é")" != "%C3%A9" ]; then echo "WARNING: This system may not encode Extended ASCII characters properly." >&2 if [ -n "$ignore_improper_extended_ascii" ]; then echo "Proceed regardless as the -e option is specified." >&2 else cat <&2 If your instance component names contain Extended ASCII characters, such as accented letters like é, this system will encode those names differently from standard encoding. If you are sure that your component names do not contain Extended ASCII characters, you may proceed regardless by running the command again with the -e option. EOD exit 1 fi fi helper=$1 if [ -z "$helper" ]; then usage fi for ext in var sed new old; do eval helper_$ext="$helper/helper.$ext" done if [ ! -s "$helper_var" ]; then error "\"$helper_var\" does not exist. \"$helper\" is probably not a Helper directory." fi . "$helper_var" profile=$aws_profile_b profile_flag=${profile:+ --profile $profile} cat < "$helper_log" # This log file is for AWS CLI tracing, not for direct execution. # Please use the $(basename $0) command (in non-dry-run mode) to perform the copying instead. # EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" # Dry-run mode - Actions listed below are not being carried out # EOD fi ############################################################ # # Prompts # cat < $TEMPOLD # Create what is in $helper_new egrep "^hour_" "$helper_new" > $TEMPNEW if [ ! -s $TEMPNEW ]; then echo "No hours of operations to create" else num_hours=$(echo $(cat $TEMPNEW | wc -l)) echo -e "\nCreating $num_hours Hours of operations" ii=0 sort $TEMPNEW | while read hour_json; do ii=$[ii+1] echo "$ii. $hour_json" hour_name=${hour_json#hour_} hour_name=${hour_name%.json} hour_name_decoded=$(path_decode "$hour_name") hour_id_a=$(jq -r ".HoursOfOperation.HoursOfOperationId" "$instance_alias_dir_a/$hour_json" | dos2unix) hour_desc=$(jq -r ".HoursOfOperation.Description | select(. != null)" "$instance_alias_dir_a/$hour_json" | dos2unix) # Description cannot be blank, or the AWS CLI will fail. if [ -z "$hour_desc" ]; then hour_desc=$hour_name_decoded fi cat "$instance_alias_dir_a/$hour_json" | jq --arg iid $instance_id_b --arg desc "$hour_desc" \ ".HoursOfOperation | del(.HoursOfOperationId, .HoursOfOperationArn) | . + { InstanceId: \$iid} | . + { Description: \$desc}" | sed -f "$helper_sed" > "$helper/$hour_json" cat <> "$helper_log" $actionLead Create hours of operation: $hour_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect create-hours-of-operation \ --cli-input-json "file://$helper/$hour_json" \ > "$helper/output_$hour_json" EOD # rm "$helper/$hour_json" continue fi aws_connect create-hours-of-operation \ --cli-input-json "file://$helper/$hour_json" \ > "$helper/output_$hour_json" || error $LINENO hour_id_b=$(jq -r ".HoursOfOperationId" "$helper/output_$hour_json" | dos2unix) aws_connect describe-hours-of-operation \ --instance-id $instance_id_b \ --hours-of-operation-id $hour_id_b \ > "$instance_alias_dir_b/$hour_json" || error $LINENO # Moving hour_json from helper_new to helper_old echo $hour_json >> "$helper_old" sed -e"/$hour_json/d" "$helper_new" > $TEMPFILE cat $TEMPFILE > "$helper_new" cat <> "$helper_sed" # Hour of operation: $hour_name_decoded s%$hour_id_a%$hour_id_b%g EOD done test $? -eq 0 || error if [ -z "$dryrun" ]; then # Update instance B hours aws_connect list-hours-of-operations \ --instance-id $instance_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r ".HoursOfOperationSummaryList[]" > "$instance_alias_dir_b/hours.json" fi fi if [ ! -s $TEMPOLD ]; then echo "No hours of operations to update" else num_hours=$(echo $(cat $TEMPOLD | wc -l)) echo -e "\nChecking $num_hours hours of operations for an update" ii=0 sort $TEMPOLD | while read hour_json; do ii=$[ii+1] echo -n "$ii. $hour_json ... " hour_name=${hour_json#hour_} hour_name=${hour_name%.json} hour_name_decoded=$(path_decode "$hour_name") cat "$instance_alias_dir_a/$hour_json" > $TEMPA cat "$instance_alias_dir_b/$hour_json" > $TEMPB df=$(diff_files); echo $df; test "$df" == "same" && continue echo "Updating $hour_json" hour_id_b=$(jq -r ".HoursOfOperation.HoursOfOperationId" "$instance_alias_dir_b/$hour_json" | dos2unix) arg_flags=$(cat "$instance_alias_dir_b/$hour_json" | jq -r ".HoursOfOperation | \"--arg id \" + .HoursOfOperationId" | dos2unix) cat "$instance_alias_dir_a/$hour_json" | jq --arg iid $instance_id_b $arg_flags \ ".HoursOfOperation | del(.HoursOfOperationArn, .Tags) | . + { InstanceId: \$iid, HoursOfOperationId: \$id }" \ > "$helper/$hour_json" cat <> "$helper_log" $actionLead Update hours of operation: $hour_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect update-hours-of-operation \ --cli-input-json "file://$helper/$hour_json" \ > "$helper/output_$hour_json" EOD # rm "$helper/$hour_json" continue fi aws_connect update-hours-of-operation \ --cli-input-json "file://$helper/$hour_json" \ > "$helper/output_$hour_json" || error $LINENO aws_connect describe-hours-of-operation \ --instance-id $instance_id_b \ --hours-of-operation-id $hour_id_b \ > "$instance_alias_dir_b/$hour_json" || error $LINENO done test $? -eq 0 || error fi ############################################################ # # Queues # cat < $TEMPOLD # Create what is in $helper_new egrep "^queue_" "$helper_new" > $TEMPNEW if [ ! -s $TEMPNEW ]; then echo "No queues to create" else num_queues=$(echo $(cat $TEMPNEW | wc -l)) echo -e "\nCreating $num_queues queues" ii=0 sort $TEMPNEW | while read queue_json; do ii=$[ii+1] echo "$ii. $queue_json" queue_name=${queue_json#queue_} queue_name=${queue_name%.json} queue_name_decoded=$(path_decode "$queue_name") queue_id_a=$(jq -r ".Queue.QueueId" "$instance_alias_dir_a/$queue_json" | dos2unix) cat "$instance_alias_dir_a/$queue_json" | jq --arg instance_id $instance_id_b \ ".Queue | del(.QueueId, .QueueArn, .OutboundCallerConfig, .Status) | . + { InstanceId: \$instance_id}" | sed -f "$helper_sed" > "$helper/$queue_json" cat <> "$helper_log" $actionLead Create queue: $queue_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect create-queue \ --cli-input-json "file://$helper/$queue_json" \ > "$helper/output_$queue_json" EOD # rm "$helper/$queue_json" continue fi aws_connect create-queue \ --cli-input-json "file://$helper/$queue_json" \ > "$helper/output_$queue_json" || error $LINENO queue_id_b=$(jq -r ".QueueId" "$helper/output_$queue_json" | dos2unix) aws_connect describe-queue \ --instance-id $instance_id_b \ --queue-id $queue_id_b \ > "$instance_alias_dir_b/$queue_json" || error $LINENO # Moving queue_json from helper_new to helper_old echo $queue_json >> "$helper_old" sed -e"/$queue_json/d" "$helper_new" > $TEMPFILE cat $TEMPFILE > "$helper_new" cat <> "$helper_sed" # Queue: $queue_name_decoded s%$queue_id_a%$queue_id_b%g EOD done test $? -eq 0 || error if [ -z "$dryrun" ]; then # Update instance B queues aws_connect list-queues \ --instance-id $instance_id_b \ --queue-types "STANDARD" \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r ".QueueSummaryList[] | select(.QueueType != \"AGENT\")" > "$instance_alias_dir_b/queues.json" fi fi if [ ! -s $TEMPOLD ]; then echo "No queues to update" else num_queues=$(echo $(cat $TEMPOLD | wc -l)) echo -e "\nChecking $num_queues queues for an update" ii=0 sort $TEMPOLD | while read queue_json; do ii=$[ii+1] echo -n "$ii. $queue_json ... " cat "$instance_alias_dir_a/$queue_json" | jq "del(.Queue.OutboundCallerConfig)" > $TEMPA cat "$instance_alias_dir_b/$queue_json" > $TEMPB df=$(diff_files); echo $df; test "$df" == "same" && continue echo "Please update $queue_json manually considering potential operation impact." done fi ############################################################ # # Routing Profiles # gen_helper_rpqr() { # Routing Profile Queue Reference routing_name=$1 out_file="$helper/routingQRs_$routing_name.json" cat "$instance_alias_dir_a/routingQs_$routing_name.json" | jq ".RoutingProfileQueueConfigSummaryList[] | { QueueReference: { QueueId, Channel }, Priority, Delay }" \ > "$out_file" echo "$out_file" } gen_helper_routing_new() { # Must not have QueueConfigs in creation, leave it to association to handle >10 cases routing_name=$1 out_file="$helper/routingNew_$routing_name.json" cat "$instance_alias_dir_a/routing_$routing_name.json" | jq --arg iid $instance_id_b \ ".RoutingProfile | del(.RoutingProfileId, .RoutingProfileArn) | . + { InstanceId: \$iid } | del(.MediaConcurrencies[] | select(.Concurrency == 0))" | sed -f "$helper_sed" > "$out_file" echo "$out_file" } cat < $TEMPOLD # Create what is in $helper_new egrep "^routing_" "$helper_new" > $TEMPNEW if [ ! -s $TEMPNEW ]; then echo "No routing profiles to create" else num_routings=$(echo $(cat $TEMPNEW | wc -l)) echo -e "\nCreating $num_routings routing profiles" ii=0 sort $TEMPNEW | while read routing_json; do ii=$[ii+1] echo "$ii. $routing_json" routing_name=${routing_json#routing_} routing_name=${routing_name%.json} routing_name_decoded=$(path_decode "$routing_name") routing_id_a=$(jq -r ".RoutingProfile.RoutingProfileId" "$instance_alias_dir_a/$routing_json" | dos2unix) routing_new_file=$(gen_helper_routing_new "$routing_name" | dos2unix) out_file="$helper/output_routing_new_$routing_name.json" cat <> "$helper_log" $actionLead Create routing profile: $routing_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect create-routing-profile \ --cli-input-json "file://$routing_new_file" \ > "$out_file" EOD # rm "$routing_new_file" continue fi aws_connect create-routing-profile \ --cli-input-json "file://$routing_new_file" \ > "$out_file" || error $LINENO routing_id_b=$(jq -r ".RoutingProfileId" "$out_file" | dos2unix) # All routing profiles will be updated with queues echo "$routing_json" >> $TEMPOLD # Update instance B (even it is not final) to allow comparison in update aws_connect describe-routing-profile \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b | jq -r "del(.RoutingProfile.NumberOfAssociatedQueues, .RoutingProfile.NumberOfAssociatedUsers)" \ > "$instance_alias_dir_b/$routing_json" || error $LINENO aws_connect list-routing-profile-queues \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ > "$instance_alias_dir_b/routingQs_$routing_name.json" || error $LINENO # Moving routing_json from helper_new to helper_old echo "$routing_json" >> "$helper_old" sed -e"/$routing_json/d" "$helper_new" > $TEMPFILE cat $TEMPFILE > "$helper_new" cat <> "$helper_sed" # Routing Profile: $routing_name_decoded s%$routing_id_a%$routing_id_b%g EOD done test $? -eq 0 || error if [ -z "$dryrun" ]; then # Update instance B routing profiles aws_connect list-routing-profiles \ --instance-id $instance_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r ".RoutingProfileSummaryList[]" > "$instance_alias_dir_b/routings.json" fi fi if [ ! -s $TEMPOLD ]; then echo "No routing profiles to update" else num_routings=$(echo $(cat $TEMPOLD | wc -l)) # echo $num_routings existing routing profiles: not to be auto-updated due to possible operation impact echo -e "\nChecking $num_routings routing profiles for an update" ii=0 sort $TEMPOLD | while read routing_json; do ii=$[ii+1] echo -n "$ii. $routing_json ... " routing_name=${routing_json#routing_} routing_name=${routing_name%.json} routing_name_decoded=$(path_decode "$routing_name") cat "$instance_alias_dir_a/$routing_json" | jq "del(.RoutingProfile.MediaConcurrencies[] | select(.Concurrency == 0))" > $TEMPA cat "$instance_alias_dir_b/$routing_json" | jq "del(.RoutingProfile.MediaConcurrencies[] | select(.Concurrency == 0))" > $TEMPB # df=$(diff_files); echo $df; test "$df" == "same" && continue df_r=$(diff_files) cat "$instance_alias_dir_a/routingQs_$routing_name.json" > $TEMPA cat "$instance_alias_dir_b/routingQs_$routing_name.json" > $TEMPB # df=$(diff_files); echo $df; test "$df" == "same" && continue df_rq=$(diff_files) if [ "$df_r" == "same" -a "$df_rq" == "same" ]; then echo same continue fi echo " routing $df_r routing-queues $df_rq" echo "Updating $routing_json" # routing_old_file=$(gen_helper_routing_old "$routing_name") routing_id_b=$(jq -r ".RoutingProfile.RoutingProfileId" "$instance_alias_dir_b/$routing_json" | dos2unix) if [ "$df_r" != "same" ]; then # Update Routing Profile of Instance B ahead of time # then update the concurrency and default-outbound-queue. # (The name must have already matched.) if [ -z "$dryrun" ]; then cat "$instance_alias_dir_a/$routing_json" | sed -f "$helper_sed" > "$instance_alias_dir_b/$routing_json" fi routing_doq_b=$(cat "$instance_alias_dir_b/$routing_json" | jq -r ".RoutingProfile.DefaultOutboundQueueId" | dos2unix) cat "$instance_alias_dir_b/$routing_json" | jq -r ".RoutingProfile.MediaConcurrencies[] | select(.Concurrency != 0)" | jq -s "." > "$helper/routingConcurrency_$routing_name.json" cat <> "$helper_log" $actionLead Update routing profile: $routing_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect update-routing-profile-default-outbound-queue \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ --default-outbound-queue-id $routing_doq_b aws connect update-routing-profile-concurrency \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ --media-concurrencies "file://$helper/routingConcurrency_$routing_name.json" EOD # rm "$helper/routingConcurrency_$routing_name.json" else aws_connect update-routing-profile-default-outbound-queue \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ --default-outbound-queue-id $routing_doq_b || error $LINENO aws_connect update-routing-profile-concurrency \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ --media-concurrencies "file://$helper/routingConcurrency_$routing_name.json" || error $LINENO aws_connect describe-routing-profile \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b | jq -r "del(.RoutingProfile.NumberOfAssociatedQueues, .RoutingProfile.NumberOfAssociatedUsers)" \ > "$instance_alias_dir_b/$routing_json" || error $LINENO fi fi if [ "$df_rq" != "same" ]; then cat "$instance_alias_dir_a/routingQs_$routing_name.json" | jq -r ".RoutingProfileQueueConfigSummaryList[].QueueName" | sort -u > $TEMPA cat "$instance_alias_dir_b/routingQs_$routing_name.json" | jq -r ".RoutingProfileQueueConfigSummaryList[].QueueName" | sort -u > $TEMPB > $TEMP1 diff $TEMPA $TEMPB | grep "^< " | sed -e"s/^< //" > $TEMP2 if [ -s $TEMP2 ]; then helper_rqc_json="$helper/routingQueueConfig_$routing_name.json" cat $TEMP2 | while read q_name; do # cat "$instance_alias_dir_a/routingQs_$routing_name.json" | # jq -r ".RoutingProfileQueueConfigSummaryList[] | select(.QueueName == \"${q_name//\"/\\\"}\") | { QueueReference: { QueueId, Channel }, Priority, Delay }" >> $TEMP1 sed -e's/\\"/%22/g' "$instance_alias_dir_a/routingQs_$routing_name.json" | jq -r ".RoutingProfileQueueConfigSummaryList[] | select(.QueueName == \"${q_name//\"/%22}\") | { QueueReference: { QueueId, Channel }, Priority, Delay }" >> $TEMP1 done cat $TEMP1 | sed -f "$helper_sed" | jq -s "." > "$helper_rqc_json" cat <> "$helper_log" $actionLead Associate queues to routing profile: $routing_name_decoded EOD num_queue_configs=$(cat "$helper_rqc_json" | jq "length") if [ -n "$dryrun" ]; then cat <> "$helper_log" fi cat <> "$helper_log" aws connect associate-routing-profile-queues \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ --queue-configs "file://$helper_rqc_json" EOD # rm "$helper_rqc_json" else # cat $TEMP2 # aws_connect associate-routing-profile-queues \ # --instance-id $instance_id_b \ # --routing-profile-id $routing_id_b \ # --queue-configs "file://$helper_rqc_json" || error $LINENO # echo $num_queue_configs Queue Configs iii=0 while [ "$iii" -lt "$num_queue_configs" ]; do jjj=$[iii+AWS_CLI_MAX_QUEUE_CONFIGS] arg_queue_configs="'$(cat "$helper_rqc_json" | jq ".[$iii:$jjj]")'" aws_connect associate-routing-profile-queues \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ --queue-configs $arg_queue_configs || error $LINENO iii=$jjj done fi fi if [ -z "$dryrun" ]; then aws_connect list-routing-profile-queues \ --instance-id $instance_id_b \ --routing-profile-id $routing_id_b \ > "$instance_alias_dir_b/routingQs_$routing_name.json" || error $LINENO fi fi done test $? -eq 0 || error fi ############################################################ # # Contact Flow Modules Creation # TODO: # - Use $helper/module_template.json as a template to create new contact flows # # Note: Must create all flow modules and flows from an empty template first # such that references can be resolved in updates # cat < $TEMPMOD # Create what is in $helper_new egrep "^module_$contact_flow_prefix_encoded" "$helper_new" > $TEMPNEW if [ ! -s $TEMPNEW ]; then echo "No contact flow modules$contact_flow_prefix_text to create" else num_modules=$(echo $(cat $TEMPNEW | wc -l)) echo -e "\nCreating $num_modules contact flow modules$contact_flow_prefix_text" ii=0 sort $TEMPNEW | while read module_json; do ii=$[ii+1] echo "$ii. $module_json" module_name=${module_json#module_} module_name=${module_name%.json} module_name_decoded=$(path_decode "$module_name") # module_id_a=$(jq -r "select(.Name == \"${module_name_decoded//\"/\\\"}\") | .Id" "$instance_alias_dir_a/modules.json") module_id_a=$(sed -e's/\\"/%22/g' "$instance_alias_dir_a/modules.json" | jq -r "select(.Name == \"${module_name_decoded//\"/%22}\") | .Id" | dos2unix) out_file="$helper/output_$module_json" # Contact Flow Module template file module_template_file=$helper/module_template.json # cat "$instance_alias_dir_a/$module_json" | # sed -f "$helper_sed" | # sub_lex_bot \ # > "$helper/$module_json" cat <> "$helper_log" $actionLead Create contact flow module: $module_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect create-contact-flow-module \ --instance-id $instance_id_b \ --name "${module_name_decoded//\"/\\\"}" \ --content "file://$module_template_file" \ > "$out_file" EOD # rm "$helper/$module_json" continue fi aws_connect create-contact-flow-module \ --instance-id $instance_id_b \ --name "${module_name_decoded//\"/\\\"}" \ --content "file://$module_template_file" \ > "$out_file" || error $LINENO module_id_b=$(jq -r ".Id" "$out_file" | dos2unix) # All flow modules will be updated with content echo "$module_json" >> $TEMPMOD aws_connect describe-contact-flow-module \ --instance-id $instance_id_b \ --contact-flow-module-id $module_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r '.ContactFlowModule.Content' > "$instance_alias_dir_b/$module_json" # Moving module_json from helper_new to helper_old echo "$module_json" >> "$helper_old" sed -e"/$module_json/d" "$helper_new" > $TEMPFILE cat $TEMPFILE > "$helper_new" cat <> "$helper_sed" # Contact Flow Module: $module_name_decoded s%$module_id_a%$module_id_b%g EOD done test $? -eq 0 || error if [ -z "$dryrun" ]; then # Update instance B flow modules aws_connect list-contact-flow-modules \ --instance-id $instance_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r ".ContactFlowModulesSummaryList[] | select(.Name | test(\"^($contact_flow_prefix|Default ).*\"))" \ > "$instance_alias_dir_b/modules.json" fi fi ############################################################ # # Contact Flows Creation # # TODO: # - Use $helper/flow_template.json as a template to create new contact flows # - Use the Default special type contact flow to create special type contact flows # - Skip "Default " and "Sample " contact flows cat < $TEMPOLD # Create what is in $helper_new egrep "^flow_$contact_flow_prefix_encoded" "$helper_new" > $TEMPNEW if [ ! -s $TEMPNEW ]; then echo "No contact flows$contact_flow_prefix_text to create" else num_flows=$(echo $(cat $TEMPNEW | wc -l)) echo -e "\nCreating $num_flows contact flows$contact_flow_prefix_text" ii=0 sort $TEMPNEW | while read flow_json; do ii=$[ii+1] echo "$ii. $flow_json" flow_name=${flow_json#flow_} flow_name=${flow_name%.json} flow_name_decoded=$(path_decode "$flow_name") # flow_type=$(jq -r "select(.Name == \"${flow_name_decoded//\"/\\\"}\") | .ContactFlowType" "$instance_alias_dir_a/flows.json") flow_type=$(sed -e's/\\"/%22/g' "$instance_alias_dir_a/flows.json" | jq -r "select(.Name == \"${flow_name_decoded//\"/%22}\") | .ContactFlowType" | dos2unix) # flow_id_a=$(jq -r "select(.Name == \"${flow_name_decoded//\"/\\\"}\") | .Id" "$instance_alias_dir_a/flows.json") flow_id_a=$(sed -e's/\\"/%22/g' "$instance_alias_dir_a/flows.json" | jq -r "select(.Name == \"${flow_name_decoded//\"/%22}\") | .Id" | dos2unix) out_file="$helper/output_$flow_json" # Contact Flow template file flow_template_file=$helper/flow_template.json case "$flow_type" in CUSTOMER_QUEUE) flow_template_file="$instance_alias_dir_b/flow_Default%20customer%20queue.json";; CUSTOMER_HOLD) flow_template_file="$instance_alias_dir_b/flow_Default%20customer%20hold.json";; CUSTOMER_WHISPER) flow_template_file="$instance_alias_dir_b/flow_Default%20customer%20whisper.json";; AGENT_HOLD) flow_template_file="$instance_alias_dir_b/flow_Default%20agent%20hold.json";; AGENT_WHISPER) flow_template_file="$instance_alias_dir_b/flow_Default%20agent%20whisper.json";; OUTBOUND_WHISPER) flow_template_file="$instance_alias_dir_b/flow_Default%20outbound.json";; AGENT_TRANSFER) flow_template_file="$instance_alias_dir_b/flow_Default%20agent%20transfer.json";; QUEUE_TRANSFER) flow_template_file="$instance_alias_dir_b/flow_Default%20queue%20transfer.json";; esac cat <> "$helper_log" $actionLead Create contact flow: $flow_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect create-contact-flow \ --instance-id $instance_id_b \ --name "${flow_name_decoded//\"/\\\"}" \ --type $flow_type \ --content "file://$flow_template_file" \ > "$out_file" EOD continue fi aws_connect create-contact-flow \ --instance-id $instance_id_b \ --name "${flow_name_decoded//\"/\\\"}" \ --type $flow_type \ --content "file://$flow_template_file" \ > "$out_file" || error $LINENO flow_id_b=$(jq -r ".ContactFlowId" "$out_file" | dos2unix) # All flows will be updated with content echo "$flow_json" >> $TEMPOLD aws_connect describe-contact-flow \ --instance-id $instance_id_b \ --contact-flow-id $flow_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r '.ContactFlow.Content' > "$instance_alias_dir_b/$flow_json" # Moving flow_json from helper_new to helper_old echo "$flow_json" >> "$helper_old" sed -e"/$flow_json/d" "$helper_new" > $TEMPFILE cat $TEMPFILE > "$helper_new" cat <> "$helper_sed" # Contact Flow: $flow_name_decoded s%$flow_id_a%$flow_id_b%g EOD done test $? -eq 0 || error if [ -z "$dryrun" ]; then # Update instance B flows aws_connect list-contact-flows \ --instance-id $instance_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r ".ContactFlowSummaryList[] | select(.Name | test(\"^($contact_flow_prefix|Default ).*\"))" \ > "$instance_alias_dir_b/flows.json" fi fi ############################################################ # # Contact Flow Modules Update # cat < $TEMPA cat "$instance_alias_dir_b/$module_json" > $TEMPB df=$(diff_files); echo $df; test "$df" == "same" && continue cat "$instance_alias_dir_a/$module_json" | sed -f "$helper_sed" | sub_lex_bot \ > "$helper/$module_json" cat <> "$helper_log" $actionLead Update contact flow module: $module_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" cat <> "$helper_log" aws connect update-contact-flow-module-content \ --instance-id $instance_id_b \ --contact-flow-module-id $module_id_b \ --content "file://$helper/$module_json" \ > "$helper/output_content_$module_json" aws connect update-contact-flow-module-metadata \ --instance-id $instance_id_b \ --contact-flow-module-id $module_id_b \ --description "${module_desc//\"/\\\"}" \ >> "$helper/output_content_$module_json" EOD # rm "$helper/$module_json" continue fi check_contact_flow $helper/$module_json if [ $? -ne 0 ]; then cat $TEMPCHK >> "$helper_log" # Continue to handle error fi aws_connect update-contact-flow-module-content \ --instance-id $instance_id_b \ --contact-flow-module-id $module_id_b \ --content "file://$helper/$module_json" \ > "$helper/output_content_$module_json" || error $LINENO aws_connect update-contact-flow-module-metadata \ --instance-id $instance_id_b \ --contact-flow-module-id $module_id_b \ --description "${module_desc//\"/\\\"}" \ >> "$helper/output_content_$module_json" || error $LINENO aws_connect describe-contact-flow-module \ --instance-id $instance_id_b \ --contact-flow-module-id $module_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r '.ContactFlowModule.Content' > "$instance_alias_dir_b/$module_json" done test $? -eq 0 || error fi ############################################################ # # Contact Flows Update # cat < $TEMPA cat "$instance_alias_dir_b/$flow_json" > $TEMPB df=$(diff_files); echo $df; test "$df" == "same" && continue cat "$instance_alias_dir_a/$flow_json" | sed -f "$helper_sed" | sub_lex_bot \ > "$helper/$flow_json" cat <> "$helper_log" $actionLead Update contact flow: $flow_name_decoded EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" cat <> "$helper_log" aws connect update-contact-flow-content \ --instance-id $instance_id_b \ --contact-flow-id $flow_id_b \ --content "file://$helper/$flow_json" \ > "$helper/output_content_$flow_json" aws connect update-contact-flow-metadata \ --instance-id $instance_id_b \ --contact-flow-id $flow_id_b \ --description "${flow_desc//\"/\\\"}" \ >> "$helper/output_content_$flow_json" EOD # rm "$helper/$flow_json" continue fi check_contact_flow $helper/$flow_json if [ $? -ne 0 ]; then cat $TEMPCHK >> "$helper_log" # Continue to handle error fi aws_connect update-contact-flow-content \ --instance-id $instance_id_b \ --contact-flow-id $flow_id_b \ --content "file://$helper/$flow_json" \ > "$helper/output_content_$flow_json" || error $LINENO aws_connect update-contact-flow-metadata \ --instance-id $instance_id_b \ --contact-flow-id $flow_id_b \ --description "${flow_desc//\"/\\\"}" \ >> "$helper/output_content_$flow_json" || error $LINENO aws_connect describe-contact-flow \ --instance-id $instance_id_b \ --contact-flow-id $flow_id_b \ > $TEMPFILE || error $LINENO cat $TEMPFILE | jq -r '.ContactFlow.Content' > "$instance_alias_dir_b/$flow_json" done test $? -eq 0 || error fi ############################################################ # # Contact flows and modules association with Lambda functions # lambdaArnLead=arn:aws:lambda:$region_b:$aws_ac_b:function: cat \ "$instance_alias_dir_b/flow_${contact_flow_prefix_encoded}"* \ "$instance_alias_dir_b/module_${contact_flow_prefix_encoded}"* 2> /dev/null | jq -r ".Actions[] | select(.Type == \"InvokeLambdaFunction\") | .Parameters.LambdaFunctionARN" | grep "$lambdaArnLead" | sort -u > $TEMPFILE if [ -s $TEMPFILE ]; then echo echo Associating Lambda functions to $instance_alias_b # Use "aws connect" instead of "aws_connect" to avoid logging aws connect list-lambda-functions \ $profile_flag \ --instance-id $instance_id_b \ > $TEMPOLD || error $LINENO cat $TEMPOLD | jq -r ".LambdaFunctions" > "$helper/lambdas.json" ii=0 cat $TEMPFILE | while read lambdaArn; do ii=$[ii+1] echo -n "$ii. $lambdaArn ... " lambdaExists=$(echo $(cat "$helper/lambdas.json" | jq ".[] | select(. == \"$lambdaArn\")" | wc -l)) if [ "$lambdaExists" -gt 0 ]; then echo "already associated" continue fi echo "to be associated" cat <> "$helper_log" $actionLead Associate Lambda function: $lambdaArn EOD if [ -n "$dryrun" ]; then cat <> "$helper_log" aws connect associate-lambda-function \ --instance-id $instance_id_b \ --function-arn $lambdaArn EOD continue fi aws_connect associate-lambda-function \ --instance-id $instance_id_b \ --function-arn $lambdaArn || error $LINENO done fi ############################################################ # # Contact flows and modules association with Lex bots # instance_alias_dir_to_discover=$instance_alias_dir_b if [ -n "$dryrun" ]; then instance_alias_dir_to_discover=$instance_alias_dir_a fi cat \ "$instance_alias_dir_to_discover/flow_${contact_flow_prefix_encoded}"* \ "$instance_alias_dir_to_discover/module_${contact_flow_prefix_encoded}"* 2> /dev/null | jq -r ".Actions[] | select(.Type == \"ConnectParticipantWithLexBot\") | .Parameters.LexBot | select(. != null) | \"Name=\" + .Name + \",LexRegion=\" + .Region" | sort -u > $TEMPFILE if [ -s $TEMPFILE ]; then echo echo "Associating Lex bots (Classic) to $instance_alias_b" cat $TEMPFILE # Use "aws connect" instead of "aws_connect" to avoid logging aws connect list-lex-bots \ $profile_flag \ --instance-id $instance_id_b \ > $TEMPOLD || error $LINENO cat $TEMPOLD | jq -r ".LexBots" > "$helper/lex-bots.json" ii=0 cat $TEMPFILE | while read lexBot; do ii=$[ii+1] echo -n "$ii. $lexBot ... " lexBotName=${lexBot%%,*} lexBotName=${lexBotName#Name=} lexBotExists=$(echo $(cat "$helper/lex-bots.json" | jq ".[] | select(.Name == \"$lexBotName\")" | wc -l)) if [ "$lexBotExists" -gt 0 ]; then echo "already associated" continue fi echo "to be associated" cat <> "$helper_log" $actionLead Associate Lex Bot (Classic): $lexBot EOD if [ -n "$dryrun" ]; then if [ -n "$lex_bot_prefix_a" -o -n "$lex_bot_prefix_b" ]; then lexBot=$(echo "$lexBot" | sed -e"s/^Name=$lex_bot_prefix_a/Name=$lex_bot_prefix_b/") fi cat <> "$helper_log" aws connect associate-lex-bot \ --instance-id $instance_id_b \ --lex-bot $lexBot EOD continue fi aws_connect associate-lex-bot \ --instance-id $instance_id_b \ --lex-bot $lexBot || error $LINENO done fi ############################################################ # # Conclusion # num_actions=$(echo $(egrep "^$actionLead" "$helper_log" | wc -l)) echo echo "$num_actions actions on the target instance" if [ "$num_actions" -eq 0 ]; then echo "Target instance is the same as the source instance. No updates are required." exit 3 else echo echo "The AWS CLI commands carrying out such actions can be found in: $helper_log" echo if [ -n "$dryrun" ]; then echo "No actions were carried out in Dry-run mode" else echo "All done" fi fi