diff --git a/aliases b/aliases index 4dd1ca2f..df9e9a32 100644 --- a/aliases +++ b/aliases @@ -89,12 +89,21 @@ alias connector-group-members='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma connector alias connector-groups='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma connector-groups' alias connectors='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma connectors' alias debug='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma debug' +alias deployment='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma deployment' alias deployment-delete-danger='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma deployment-delete-danger' alias deployment-groups='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma deployment-groups' +alias deployments='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma deployments' alias deployments-group='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma deployments-group' alias distributions='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma distributions' alias ecr-repositories='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ecr-repositories' alias ecr-repository-images='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ecr-repository-images' +alias ecs-clusters2='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ecs-clusters2' +alias ecs-scaling-actions='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ecs-scaling-actions' +alias ecs-scaling-activities='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ecs-scaling-activities' +alias ecs-services2='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ecs-services2' +alias ecs-tasks='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ecs-tasks' +alias elasticache-replication-groups='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elasticache-replication-groups' +alias elasticaches='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elasticaches' alias elb-azs='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elb-azs' alias elb-dnsname='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elb-dnsname' alias elb-instances='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elb-instances' @@ -108,6 +117,10 @@ alias elbv2-dnsname='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elbv2-dnsname' alias elbv2-subnets='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elbv2-subnets' alias elbv2-target-groups='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elbv2-target-groups' alias elbv2s='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma elbv2s' +alias fargate-clusters='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma fargate-clusters' +alias fargate-services='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma fargate-services' +alias fargate-tasks='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma fargate-tasks' +alias hosted-zone-a-records='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma hosted-zone-a-records' alias hosted-zone-ns-records='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma hosted-zone-ns-records' alias hosted-zones='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma hosted-zones' alias iam-role-principal='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma iam-role-principal' @@ -121,6 +134,7 @@ alias instance-console='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-console alias instance-dns='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-dns' alias instance-health-set-unhealthy='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-health-set-unhealthy' alias instance-iam-profile='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-iam-profile' +alias instance-id='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-id' alias instance-ip='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-ip' alias instance-profile='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-profile' alias instance-profile-role='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma instance-profile-role' @@ -200,7 +214,9 @@ alias ssm-association-execution-targets='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma alias ssm-association-executions='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-association-executions' alias ssm-associations='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-associations' alias ssm-automation-execution='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-automation-execution' +alias ssm-automation-execution-failures='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-automation-execution-failures' alias ssm-automation-executions='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-automation-executions' +alias ssm-automation-step-executions='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-automation-step-executions' alias ssm-instances='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-instances' alias ssm-parameter-value='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-parameter-value' alias ssm-parameters='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma ssm-parameters' @@ -212,7 +228,10 @@ alias stack-asgs='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-asgs' alias stack-cancel-update='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-cancel-update' alias stack-create='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-create' alias stack-delete='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-delete' +alias stack-describe-drift='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-describe-drift' +alias stack-detect-drift='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-detect-drift' alias stack-diff='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-diff' +alias stack-diff-drift='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-diff-drift' alias stack-elbs='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-elbs' alias stack-events='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-events' alias stack-exports='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma stack-exports' @@ -242,6 +261,7 @@ alias tag-keys='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma tag-keys' alias tag-values='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma tag-values' alias target-group-targets='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma target-group-targets' alias target-groups='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma target-groups' +alias trim_date='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma trim_date' alias vpc-az-count='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma vpc-az-count' alias vpc-azs='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma vpc-azs' alias vpc-default-delete='${BMA_HOME:-$HOME/.bash-my-aws}/bin/bma vpc-default-delete' diff --git a/bash_completion.sh b/bash_completion.sh index 6751c2b8..2c40112d 100644 --- a/bash_completion.sh +++ b/bash_completion.sh @@ -202,6 +202,7 @@ complete -F _bma_instances_completion instance-console complete -F _bma_instances_completion instance-dns complete -F _bma_instances_completion instance-health-set-unhealthy complete -F _bma_instances_completion instance-iam-profile +complete -F _bma_instances_completion instance-id complete -F _bma_instances_completion instance-ip complete -F _bma_instances_completion instance-profile complete -F _bma_instances_completion instance-profile-role @@ -240,7 +241,10 @@ complete -F _bma_stacks_completion stack-asg-instances complete -F _bma_stacks_completion stack-asgs complete -F _bma_stacks_completion stack-cancel-update complete -F _bma_stacks_completion stack-delete +complete -F _bma_stacks_completion stack-describe-drift +complete -F _bma_stacks_completion stack-detect-drift complete -F _bma_stacks_completion stack-diff +complete -F _bma_stacks_completion stack-diff-drift complete -F _bma_stacks_completion stack-elbs complete -F _bma_stacks_completion stack-events complete -F _bma_stacks_completion stack-exports diff --git a/docs/command-reference.md b/docs/command-reference.md index 5a632379..192194de 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -408,7 +408,7 @@ Show all events for CF stack until update completes or fails. ### stack-template -Return template of a stack +Return template of each stack ### stack-template-changeset-latest @@ -437,6 +437,21 @@ List outputs of a stack Validate a stack template +### stack-detect-drift + +Detect drift for provided stacks; and print coloured diff + + +### stack-describe-drift + +List stack-tags applied to a stack + + +### stack-diff-drift + +List stack-tags applied to a stack + + ### stack-diff Compare live stack against local template (and optional params file) @@ -518,6 +533,13 @@ List EC2 Instances i-806d8f1592e2a2efd ami-123456789012 t3.nano running postgres2 2019-12-10T08:17:22.000Z ap-southeast-2a None +### instance-id + +Just return the instance ID of whatever was passed in (so you can run a for loop, for instance) + + USAGE: instances [grep] | instance-id + + ### instance-asg List autoscaling group membership of EC2 Instance(s) @@ -898,7 +920,16 @@ List CloudFormation stack for asg(s) List scaling activities for Autoscaling Group(s) -azure.azcli +## autoscaling-commands + + +### ##scaling-ecs + +List autoscaling actions +filter by environment (eg test1) or namespace (eg ecs) +if you pass an argument, it'll filter for clusters whose ARN contains your text + + $ scaling-ecs 'test.*down' # list the scale-down times of all our test environments ## azure-commands @@ -1230,6 +1261,9 @@ List routes of all endpoints for Front Door Profile(s) +azure.azcli + + ## backup-commands @@ -1319,6 +1353,43 @@ List logging status of Cloudtrails USAGE: cloudtrail-status cloudtrail [cloudtrail] +## codedeploy-commands + + +### deployment + +List deployments + + +### deployments + +List all deployment IDs for a deployment group (not useful for the user, only internal) +# ?? if no deployment group, could we list all deployments for this application, with their groups and statuses? + + +### deployment-groups + +List all deployment groups for an application + + +## codedeploy-commands~ + + +### deployment + +List deployments + + +### deployments + +List all deployment IDs for a deployment group (not useful for the user, only internal) + + +### deployment-groups + +List all deployment groups for an application + + ## ecr-commands @@ -1332,6 +1403,151 @@ List ECR Repositories List images for ECR Repositories +## ecs-commands + +List ECS clusters +output includes clusterName,status,activeServicesCount,runningTasksCount,pendingTasksCount +if you pass an argument, it'll filter for clusters whose ARN contains your text + + $ ecs-clusters test + test-octopus-ecs-cluster ACTIVE 1 1 0 + test1-ecs-cluster ACTIVE 3 1 0 + test3-ecs-cluster ACTIVE 3 1 0 + test2-ecs-cluster ACTIVE 3 3 0 + + +### ecs-services + +List ECS services +output includes serviceName,status,desiredCount,runningCount,pendingCount,createdAt + +gets all clusters if no filter passed in +if you do pass a filter: +1. if your filter is the name of one of your clusters, it will list the services in that cluster (eg ecs-clusters test1 | ecs-services) +2. if your filter is not a cluster name, it will list the services in all clusters whose names match your filter (ie it filters on cluster name not service name) +3. if you do not pass a filter, it will list all services in all clusters + + $ ecs-clusters test1|ecs-services + test1-ecs-admin-7URaUr0YGJHi ACTIVE 0 0 0 2023-09-13T17:16:48.198000+10:00 + test1-ecs-public-wEaTAqGXqbpq ACTIVE 0 0 0 2023-09-13T16:54:54.162000+10:00 + test1-ecs-hangfire-YNIo1hlx8rjn ACTIVE 1 1 0 2023-09-13T16:39:06.218000+10:00 + + +### ecs-tasks + +List ECS tasks +output includes taskDefinitionArn, createdAt, cpu, memory + +gets all tasks if no filter passed in +if you do pass a filter, it filters on the task name. All clusters are included (I haven't worked out a way of passing a cluster name AND a filter) + + $ ecs-tasks test2 + arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-public:18 2023-09-19T17:51:56.418000+10:00 2048 4096 + arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-admin:20 2023-08-29T10:03:36.956000+10:00 2048 4096 + arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-hangfire:22 2023-09-19T17:11:06.622000+10:00 1024 2048 + + +### ecs-scaling-activities + +LIst autoscaling activities - the actual scaling events that have happened +eg +ecs-scaling www +2023-11-22T06:24:50.937000+11:00 www-ecs-public-ServicePublic-OuN3rXBLvmx3-AlarmLow-64de4512-d901-4b26-a6a2-184bb1e90bc6 in state ALARM triggered policy www-ecs-public-target-tracking-mem70 Successfully set desired count to 2. Change successfully fulfilled by ecs. +2023-11-22T05:25:48.611000+11:00 www-ecs-public-ServicePublic-OuN3rXBLvmx3-AlarmHigh-6408c172-647e-4c0e-aac9-a800cd83317d in state ALARM triggered policy www-ecs-public-target-tracking-mem70 Successfully set desired count to 3. Change successfully fulfilled by ecs. + + +### ecs-scaling-actions + +List autoscaling actions - cron-based scheduled scaling +filter by environment (eg test1) or namespace (eg ecs) +if you pass an argument, it'll filter for clusters whose ARN contains your text + + $ scaling-ecs 'test.*down' # list the scale-down times of all our test environments + + +## elasticache-commands + + +### elasticaches + +List elasticache thingies (code borrowed from target-groups) + + $ target-groups + bash-my-aws-nlb-tg TCP 22 vpc-04636ebe5573f6f65 instance bash-my-aws-nlb + bash-my-aws-alb-tg HTTP 443 vpc-04636ebe5573f6f65 instance bash-my-aws-alb + + +### elasticache-replication-groups + + +Accepts a string to filter on +This is not very useful without column headings. +Most of the things you want to know about a replication group are boolean +eg AutomaticFailover, MultiAZClusterEnabled, AtRestEncryptionEnabled etc + + +### ecs-clusters + +List ECS clusters +output includes clusterName,status,activeServicesCount,runningTasksCount,pendingTasksCount +if you pass an argument, it'll filter for clusters whose ARN contains your text + + $ ecs-clusters test + test-octopus-ecs-cluster ACTIVE 1 1 0 + test1-ecs-cluster ACTIVE 3 1 0 + test3-ecs-cluster ACTIVE 3 1 0 + test2-ecs-cluster ACTIVE 3 3 0 + + +### ecs-services + +List ECS services +output includes serviceName,status,desiredCount,runningCount,pendingCount,createdAt + +gets all clusters if no filter passed in +if you do pass a filter: +1. if your filter is the name of one of your clusters, it will list the services in that cluster (eg ecs-clusters test1 | ecs-services) +2. if your filter is not a cluster name, it will list the services in all clusters whose names match your filter (ie it filters on cluster name not service name) +3. if you do not pass a filter, it will list all services in all clusters + + $ ecs-clusters test1|ecs-services + test1-ecs-admin-7URaUr0YGJHi ACTIVE 0 0 0 2023-09-13T17:16:48.198000+10:00 + test1-ecs-public-wEaTAqGXqbpq ACTIVE 0 0 0 2023-09-13T16:54:54.162000+10:00 + test1-ecs-hangfire-YNIo1hlx8rjn ACTIVE 1 1 0 2023-09-13T16:39:06.218000+10:00 + + +### ecs-tasks + +List ECS tasks +output includes taskDefinitionArn, createdAt, cpu, memory + +gets all tasks if no filter passed in +if you do pass a filter, it filters on the task name. All clusters are included (I haven't worked out a way of passing a cluster name AND a filter) + + $ ecs-tasks test2 + arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-public:18 2023-09-19T17:51:56.418000+10:00 2048 4096 + arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-admin:20 2023-08-29T10:03:36.956000+10:00 2048 4096 + arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-hangfire:22 2023-09-19T17:11:06.622000+10:00 1024 2048 + + +### ecs-scaling-activities + +LIst autoscaling activities - the actual scaling events that have happened +eg +ecs-scaling www +2023-11-22T06:24:50.937000+11:00 www-ecs-public-ServicePublic-OuN3rXBLvmx3-AlarmLow-64de4512-d901-4b26-a6a2-184bb1e90bc6 in state ALARM triggered policy www-ecs-public-target-tracking-mem70 Successfully set desired count to 2. Change successfully fulfilled by ecs. +2023-11-22T05:25:48.611000+11:00 www-ecs-public-ServicePublic-OuN3rXBLvmx3-AlarmHigh-6408c172-647e-4c0e-aac9-a800cd83317d in state ALARM triggered policy www-ecs-public-target-tracking-mem70 Successfully set desired count to 3. Change successfully fulfilled by ecs. + + +### ecs-scaling-actions + +List autoscaling actions - cron-based scheduled scaling +filter by environment (eg test1) or namespace (eg ecs) +if you pass an argument, it'll filter for clusters whose ARN contains your text + + $ scaling-ecs 'test.*down' # list the scale-down times of all our test environments + + ## elb-commands @@ -1468,6 +1684,28 @@ List target groups of ELBv2(s) [Application and Network Load Balancers) bash-my-aws-alb-tg HTTP 443 vpc-018d9739 bash-my-aws-alb +## fargate-commands + + +### fargate-clusters + +List ECS clusters + + +### fargate-services + +List ECS services +gets all clusters if no cluster_names passed in +echo "cluster_names=$cluster_names" + + +### fargate-tasks + +List ECS services +gets all clusters if no cluster_names passed in +echo "service_names=$service_names" + + ## iam-commands @@ -1782,6 +2020,44 @@ List RDS Database Clusters ## route53-commands +### hosted-zones + +List Route53 Hosted Zones + + $ hosted-zones + /hostedzone/Z3333333333333 5 NotPrivateZone bash-my-aws.org. + /hostedzone/Z5555555555555 2 NotPrivateZone bash-my-universe.com. + /hostedzone/Z4444444444444 3 NotPrivateZone bashmyaws.org. + /hostedzone/Z1111111111111 3 NotPrivateZone bash-my-aws.com. + /hostedzone/Z2222222222222 3 NotPrivateZone bashmyaws.com. + + +### hosted-zone-ns-records + +Generate NS records for delegating domain to AWS + + $ hosted-zones bash-my-aws.org + /hostedzone/ZJ6ZCG2UD6OKX 5 NotPrivateZone bash-my-aws.org. + + $ hosted-zones bash-my-aws.org | hosted-zone-ns-records + bash-my-aws.org. 300 IN NS ns-786.awsdns-34.net. + bash-my-aws.org. 300 IN NS ns-1549.awsdns-01.co.uk. + bash-my-aws.org. 300 IN NS ns-362.awsdns-45.com. + bash-my-aws.org. 300 IN NS ns-1464.awsdns-55.org. + + +### hosted-zone-a-records + +Generate NS records for delegating domain to AWS + + $ hosted-zone-a-records bash-my-aws.org + + $ hosted-zones | hosted-zone-a-records + + +## route53-commandsTEMP + + ### hosted-zones List Route53 Hosted Zones @@ -1928,24 +2204,24 @@ Run a command locally on EC2 instance(s) running Windows $ ssm-instances Windows | ssm-send-command-windows Get-Hotfix Command ID: a0eeeddc-2edf-42bc-b0c7-122f5bc50956 Waiting for command to complete... - i-0fake1234abcd - Source Description HotFixID InstalledBy InstalledOn - ------ ----------- -------- ----------- ----------- - FAKEAPP01234 Update KB1234567 NT AUTHORITY\SYSTEM 10/11/2023 12:00:00 AM - FAKEAPP01234 Update KB8901234 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM - FAKEAPP01234 Security Update KB5678901 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM - FAKEAPP01234 Update KB2345678 NT AUTHORITY\SYSTEM 1/9/2019 12:00:00 AM - FAKEAPP01234 Update KB3456789 NT AUTHORITY\SYSTEM 3/11/2021 12:00:00 AM - FAKEAPP01234 Security Update KB4567890 NT AUTHORITY\SYSTEM 4/21/2019 12:00:00 AM - FAKEAPP01234 Security Update KB5678901 NT AUTHORITY\SYSTEM 5/15/2019 12:00:00 AM - FAKEAPP01234 Security Update KB6789012 NT AUTHORITY\SYSTEM 6/12/2019 12:00:00 AM - ---Output truncated--- - i-0fake1234abcd - Source Description HotFixID InstalledBy InstalledOn - ------ ----------- -------- ----------- ----------- - FAKEAPP01234 Update KB1234567 NT AUTHORITY\SYSTEM 10/11/2023 12:00:00 AM - FAKEAPP01234 Update KB8901234 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM - FAKEAPP01234 Security Update KB5678901 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM + i-0fake1234abcd + Source Description HotFixID InstalledBy InstalledOn + ------ ----------- -------- ----------- ----------- + FAKEAPP01234 Update KB1234567 NT AUTHORITY\SYSTEM 10/11/2023 12:00:00 AM + FAKEAPP01234 Update KB8901234 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM + FAKEAPP01234 Security Update KB5678901 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM + FAKEAPP01234 Update KB2345678 NT AUTHORITY\SYSTEM 1/9/2019 12:00:00 AM + FAKEAPP01234 Update KB3456789 NT AUTHORITY\SYSTEM 3/11/2021 12:00:00 AM + FAKEAPP01234 Security Update KB4567890 NT AUTHORITY\SYSTEM 4/21/2019 12:00:00 AM + FAKEAPP01234 Security Update KB5678901 NT AUTHORITY\SYSTEM 5/15/2019 12:00:00 AM + FAKEAPP01234 Security Update KB6789012 NT AUTHORITY\SYSTEM 6/12/2019 12:00:00 AM + ---Output truncated--- + i-0fake1234abcd + Source Description HotFixID InstalledBy InstalledOn + ------ ----------- -------- ----------- ----------- + FAKEAPP01234 Update KB1234567 NT AUTHORITY\SYSTEM 10/11/2023 12:00:00 AM + FAKEAPP01234 Update KB8901234 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM + FAKEAPP01234 Security Update KB5678901 NT AUTHORITY\SYSTEM 12/12/2018 12:00:00 AM See also: ssm-send-command-windows @@ -1963,6 +2239,20 @@ USAGE: ssm-automation-executions [filter] ghijqrst-uvwx-2345-yzab-abcd5678efgh UpdateAndSecureNodes i-3d4e5f6g7h8i90123 Failed 2023-07-20T09:00:40.000000+00:00 None +### ssm-automation-execution-failures + + + +### ssm-automation-step-executions + +Show step-by-step details for an SSM Automation Execution + + USAGE: automation-execution-steps execution_id [execution_id] + + $ ssm-automation-executions | ssm-automation-steps-executions + [Outputs detailed step information for each provided execution ID] + + ### ssm-automation-execution Show details for an SSM Automation Execution diff --git a/functions b/functions index 90476f77..ca9e31a7 100644 --- a/functions +++ b/functions @@ -89,12 +89,21 @@ connector-group-members connector-groups connectors debug +deployment deployment-delete-danger deployment-groups +deployments deployments-group distributions ecr-repositories ecr-repository-images +ecs-clusters2 +ecs-scaling-actions +ecs-scaling-activities +ecs-services2 +ecs-tasks +elasticache-replication-groups +elasticaches elb-azs elb-dnsname elb-instances @@ -108,6 +117,10 @@ elbv2-dnsname elbv2-subnets elbv2-target-groups elbv2s +fargate-clusters +fargate-services +fargate-tasks +hosted-zone-a-records hosted-zone-ns-records hosted-zones iam-role-principal @@ -121,6 +134,7 @@ instance-console instance-dns instance-health-set-unhealthy instance-iam-profile +instance-id instance-ip instance-profile instance-profile-role @@ -201,7 +215,9 @@ ssm-association-execution-targets ssm-association-executions ssm-associations ssm-automation-execution +ssm-automation-execution-failures ssm-automation-executions +ssm-automation-step-executions ssm-instances ssm-parameter-value ssm-parameters @@ -213,7 +229,10 @@ stack-asgs stack-cancel-update stack-create stack-delete +stack-describe-drift +stack-detect-drift stack-diff +stack-diff-drift stack-elbs stack-events stack-exports @@ -243,6 +262,7 @@ tag-keys tag-values target-group-targets target-groups +trim_date vpc-az-count vpc-azs vpc-default-delete diff --git a/lib/autoscaling-functions b/lib/autoscaling-functions new file mode 100644 index 00000000..b98b02c6 --- /dev/null +++ b/lib/autoscaling-functions @@ -0,0 +1,44 @@ +#!/bin/bash +# +# autoscaling-functions +## +# show the current autoscale settings +# namespace | implemented +# ecs | yes +# elasticmapreduce | no +# ec2 | maybe +# appstream | maybe +# dynamodb | no +# rds | maybe +# sagemaker | no +# custom-resource | no +# comprehend | no +# lambda | no +# cassandra | no +# kafka | no +# elasticache | no +# neptune | no + +### this has been moved to the ecs functions. was that a good idea? +##scaling-ecs() { + # List autoscaling actions + # filter by environment (eg test1) or namespace (eg ecs) + # if you pass an argument, it'll filter for clusters whose ARN contains your text + # + # $ scaling-ecs 'test.*down' # list the scale-down times of all our test environments +# test1-ecs-admin-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test1-ecs-public-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test2-ecs-admin-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test2-ecs-public-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test3-ecs-admin-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test3-ecs-public-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 + +## local filters=$(__bma_read_filters $@) # Filter output by arguments (optional) +## aws application-autoscaling describe-scheduled-actions \ +## --service-namespace ecs \ +## --query "ScheduledActions[].[ScheduledActionName,Schedule,ScalableTargetAction.MinCapacity,ScalableTargetAction.MaxCapacity]" \ +## --output text | +## grep -E -- "$filters" | +## LC_ALL=C sort -b -k 2| +## columnise +##} diff --git a/lib/ecr-functions b/lib/ecr-functions index e5db48ce..8ad317c8 100644 --- a/lib/ecr-functions +++ b/lib/ecr-functions @@ -34,6 +34,8 @@ ecr-repository-images() { local repository_names=$(skim-stdin "$@") # XXX Display USAGE if no repository_names passed in + local repository_names=$(skim-stdin "$@") + [[ -z ${repository_names} ]] && __bma_usage "ecr" && return 1 local repository_name for repository_name in $repository_names; do diff --git a/lib/ecs-functions b/lib/ecs-functions new file mode 100644 index 00000000..248c400a --- /dev/null +++ b/lib/ecs-functions @@ -0,0 +1,191 @@ +#!/bin/bash +# +# ecs-functions +## +# Shortcut the complicated dependencies between clusters, services, and tasks in the ecs cli +# this is against the normal way bash-my-aws works +# it should be +# ecs-tasks [cluster-name] +# ecs-services [cluster-name] +# but then i don't know how to filter on the task/service name + +ecs-clusters2() { + # List ECS clusters + # output includes clusterName,status,activeServicesCount,runningTasksCount,pendingTasksCount + # if you pass an argument, it'll filter for clusters whose ARN contains your text + # + # $ ecs-clusters test + # test-octopus-ecs-cluster ACTIVE 1 1 0 + # test1-ecs-cluster ACTIVE 3 1 0 + # test3-ecs-cluster ACTIVE 3 1 0 + # test2-ecs-cluster ACTIVE 3 3 0 + + local cluster=$(__bma_read_filters $@) # Filter output by arguments (optional) +# echo "cluster=$cluster" + aws ecs describe-clusters \ + --clusters $(aws ecs list-clusters --query "clusterArns[?contains(@, \`${cluster}\`) == \`true\`]" --output text) \ + --query "clusters[].[clusterName,status,activeServicesCount,runningTasksCount,pendingTasksCount]" \ + --output text | + LC_ALL=C sort -b -k 2| + columnise +} + +ecs-services2() { + + # List ECS services + # output includes serviceName,status,desiredCount,runningCount,pendingCount,createdAt + # + # gets all clusters if no filter passed in + # if you do pass a filter: + # 1. if your filter is the name of one of your clusters, it will list the services in that cluster (eg ecs-clusters test1 | ecs-services) + # 2. if your filter is not a cluster name, it will list the services in all clusters whose names match your filter (ie it filters on cluster name not service name) + # 3. if you do not pass a filter, it will list all services in all clusters + # + # $ ecs-clusters test1|ecs-services + # test1-ecs-admin-7URaUr0YGJHi ACTIVE 0 0 0 2023-09-13T17:16:48.198000+10:00 + # test1-ecs-public-wEaTAqGXqbpq ACTIVE 0 0 0 2023-09-13T16:54:54.162000+10:00 + # test1-ecs-hangfire-YNIo1hlx8rjn ACTIVE 1 1 0 2023-09-13T16:39:06.218000+10:00 + + local filter=$(skim-stdin "$@") + echo "searching with $filter" + local cluster_names=$filter +# check for input that isn't a valid cluster name and use it as a regex... + if [[ -z "$cluster_names" || "$(aws ecs describe-clusters --clusters $cluster_names --query "failures[].reason" --output text)" == "MISSING" ]]; then + echo "cluster not found, using input to search" + cluster_names=$(ecs-clusters2 $cluster_names |skim-stdin) + if [[ -z "$cluster_names" ]]; then + echo "no matching cluster, using input as a service filter" + cluster_names=$(ecs-clusters2 |skim-stdin) + service_filter=$filter + fi + fi + + +### wait that wasn't right. +## if $filter is empty just get all the clusters +## if $filter is not empty, loop through $filter to check for valid complete cluster names +## if there is one, skip the rest +## search for cluster names containing $filter +# if [[ -z "$cluster_names" ]]; then +# echo "no matching cluster, passing all clusters to search" +# cluster_names=$(ecs-clusters |skim-stdin) +# ## here we should loop through the $filter checking that each element is a cluster?, in case the ecs-clusters command returned more than one + ##for cluster_name in $(echo $cluster_names); do # other scripts don't need this echo?? +# elif [[ "$(aws ecs describe-clusters --clusters $cluster_names --query "failures[].reason" --output text)" == "MISSING" ]]; then +# echo "cluster not found, using input to search" +# cluster_names=$(ecs-clusters $cluster_names |skim-stdin) +# service_filter=$filter +# fi +# echo "cluster_names=$cluster_names" +# echo "cluster_names=$cluster_names" + + local cluster_name + for cluster_name in $(echo $cluster_names); do # other scripts don't need this echo?? +# echo "cluster_name=$cluster_name" + aws ecs list-services \ + --cluster "$cluster_name" \ + --query " + serviceArns[?contains(@, \`${service_filter}\`) == \`true\`] + " \ + --output text | + xargs -d '\t' -I {} echo {} | + sed 's/.*\///' | + xargs -I {} \ + aws ecs describe-services \ + --cluster $cluster_name \ + --services {} \ + --query " + services[].[ + serviceName,status,desiredCount,runningCount,pendingCount,createdAt + ]" \ + --output text | + columnise +# --query "services[].[serviceName,status,desiredCount,runningCount,pendingCount,taskDefinition,createdAt,deployments[0].createdAt]" \ + done +} + +ecs-tasks() { + + # List ECS tasks + # output includes taskDefinitionArn, createdAt, cpu, memory + # + # gets all tasks if no filter passed in + # if you do pass a filter, it filters on the task name. All clusters are included (I haven't worked out a way of passing a cluster name AND a filter) + # + # $ ecs-tasks test2 + # arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-public:18 2023-09-19T17:51:56.418000+10:00 2048 4096 + # arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-admin:20 2023-08-29T10:03:36.956000+10:00 2048 4096 + # arn:aws:ecs:ap-southeast-2:xxxxxxxxxxxx:task-definition/test2-hangfire:22 2023-09-19T17:11:06.622000+10:00 1024 2048 + + local filter=$(skim-stdin "$@") + + for cluster_name in $(ecs-clusters2 |skim-stdin); do + #echo "cluster_name=$cluster_name" + # TODO: remove the account bit from the task definition, ie print gc3-test1-public:27 instead of arn:aws:ecs:ap-southeast-2:167642850091:task-definition/gc3-test1-public:27 + aws ecs describe-tasks \ + --cluster $cluster_name \ + --query " + tasks[?contains(taskDefinitionArn, \`$filter\`) == \`true\`] + .[taskDefinitionArn, lastStatus, createdAt, cpu, memory, containers[].networkInterfaces[].privateIpv4Address] + " \ + --tasks $( \ + aws ecs list-tasks \ + --cluster $cluster_name \ + --output text | + sed 's/.*\///' \ + ) \ + --output text | + sed 's/arn.*task-definition\///g' | + columnise + done + +# --query 'tasks[].{createdAt: createdAt,startedAt: startedAt,cpu: cpu,memory: memory,taskDefinitionArn: taskDefinitionArn,ephemeralStorage: ephemeralStorage.sizeInGiB,privateIpv4Address: containers[0].networkInterfaces[0].privateIpv4Address}' \ +} + +ecs-scaling-activities() { + + # LIst autoscaling activities - the actual scaling events that have happened + # eg + # ecs-scaling www + # 2023-11-22T06:24:50.937000+11:00 www-ecs-public-ServicePublic-OuN3rXBLvmx3-AlarmLow-64de4512-d901-4b26-a6a2-184bb1e90bc6 in state ALARM triggered policy www-ecs-public-target-tracking-mem70 Successfully set desired count to 2. Change successfully fulfilled by ecs. + # 2023-11-22T05:25:48.611000+11:00 www-ecs-public-ServicePublic-OuN3rXBLvmx3-AlarmHigh-6408c172-647e-4c0e-aac9-a800cd83317d in state ALARM triggered policy www-ecs-public-target-tracking-mem70 Successfully set desired count to 3. Change successfully fulfilled by ecs. + + # TODO : the list can be very long, add max-items or even better implement generic paging for BMA + local filter=$(skim-stdin "$@") + +aws application-autoscaling describe-scaling-activities \ + --service-namespace ecs \ + --query " + ScalingActivities[].[StartTime, Description, Cause] + " \ + --output text | + # --resource-id # add cluster and service name for better filtering + grep -E -- "$filters" | + sed 's/monitor alarm TargetTracking-service\/.*\///' | + trim_date | + columnise + +} + + +ecs-scaling-actions() { + # List autoscaling actions - cron-based scheduled scaling + # filter by environment (eg test1) or namespace (eg ecs) + # if you pass an argument, it'll filter for clusters whose ARN contains your text + # + # $ scaling-ecs 'test.*down' # list the scale-down times of all our test environments +# test1-ecs-admin-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test1-ecs-public-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test2-ecs-admin-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test2-ecs-public-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test3-ecs-admin-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 +# test3-ecs-public-scale-down-overnight cron(0 0 9 ? * MON-FRI *) 0 0 + + local filters=$(__bma_read_filters $@) # Filter output by arguments (optional) + aws application-autoscaling describe-scheduled-actions \ + --service-namespace ecs \ + --query "ScheduledActions[].[ScheduledActionName,Schedule,ScalableTargetAction.MinCapacity,ScalableTargetAction.MaxCapacity]" \ + --output text | + grep -E -- "$filters" | + columnise +} diff --git a/lib/elasticache-functions b/lib/elasticache-functions new file mode 100644 index 00000000..ca0b1b89 --- /dev/null +++ b/lib/elasticache-functions @@ -0,0 +1,56 @@ +#!/bin/bash +# +# elasticache-functions +# +# + + +elasticaches() { + + # List elasticache thingies (code borrowed from target-groups) + # + # $ target-groups + # bash-my-aws-nlb-tg TCP 22 vpc-04636ebe5573f6f65 instance bash-my-aws-nlb + # bash-my-aws-alb-tg HTTP 443 vpc-04636ebe5573f6f65 instance bash-my-aws-alb + + local cache_names=$(skim-stdin) + local filters=$(__bma_read_filters $@) + + aws elasticache describe-cache-clusters \ + --output text \ + --query " + CacheClusters[][ + CacheClusterId, + CacheNodeType, + Engine, + EngineVersion, + CacheClusterStatus + ]" | + grep -E -- "$filters" | + LC_ALL=C sort -b | + column -s$'\t' -t +} + + +elasticache-replication-groups() { + + # + # Accepts a string to filter on + # This is not very useful without column headings. + # Most of the things you want to know about a replication group are boolean + # eg AutomaticFailover, MultiAZClusterEnabled, AtRestEncryptionEnabled etc + + local ec_names=$(skim-stdin "$@") + + for ec_name in $ec_names; do +# echo "ec_name=$ec_name" + aws elasticache describe-replication-groups \ + --output text \ + --query " + ReplicationGroups + [?contains(ReplicationGroupId, \`$ec_name\`) == \`true\`] + .[ReplicationGroupId,Status, CacheNodeType] + " + done | column -s$'\t' -t +} +# how to get ReplicationGroupId etc?? \ No newline at end of file diff --git a/lib/instance-functions b/lib/instance-functions index 1723fb0e..cd80ee13 100644 --- a/lib/instance-functions +++ b/lib/instance-functions @@ -22,6 +22,8 @@ instances() { local instances=$(skim-stdin) local filters=$(__bma_read_filters $@) + local sort_key=5 + [ -z $filters ] && sort_key=6 aws ec2 describe-instances \ ${instances/#/'--instance-ids '} \ @@ -34,14 +36,26 @@ instances() { State.Name, [Tags[?Key=='Name'].Value][0][0], LaunchTime, - Placement.AvailabilityZone, - VpcId + Placement.AvailabilityZone ]" | grep -E -- "$filters" | LC_ALL=C sort -t$'\t' -k 6 | columnise } + +instance-id() { + + # Just return the instance ID of whatever was passed in (so you can run a for loop, for instance) + # + # USAGE: instances [grep] | instance-id + + local instance_ids=$(skim-stdin "$@") + [[ -z $instance_ids ]] && __bma_usage "instance-id [instance-id]" && return 1 + echo $instance_ids | + column -s$'\t' -t +} + instance-asg() { # List autoscaling group membership of EC2 Instance(s) @@ -85,6 +99,7 @@ instance-az() { Reservations[].Instances[][ [ InstanceId, + VpcId, Placement.AvailabilityZone ] ][]" | @@ -622,7 +637,7 @@ instance-tag-create() { aws ec2 create-tags \ --resources $instances \ - --tags "Key=$key,Value=$value" + --tags "Key=$key,Value=$value" } instance-tag-delete() { @@ -638,7 +653,7 @@ instance-tag-delete() { aws ec2 delete-tags \ --resources $instances \ - --tags "Key=$key" + --tags "Key=$key" } instance-terminate() { diff --git a/lib/misc-functions b/lib/misc-functions index 6102aa49..c35d807d 100644 --- a/lib/misc-functions +++ b/lib/misc-functions @@ -20,3 +20,7 @@ columnise() { column -t -s $'\t' fi } + +trim_date() { + sed 's/\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}T[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}\)\S*/\1/g' +} diff --git a/lib/rds-functions b/lib/rds-functions index 479e17bd..20ac25e6 100644 --- a/lib/rds-functions +++ b/lib/rds-functions @@ -21,12 +21,15 @@ rds-db-instances() { --output text \ --query "DBInstances[].[ DBInstanceIdentifier, + DBInstanceClass, Engine, EngineVersion, DBInstanceStatus, DBName, + MultiAZ, DBClusterIdentifier ]" | + sed 's/True$/MultiAZ/g' | sed 's/False$//' | grep -E -- "$filters" | LC_ALL=C sort -b -k 1 | columnise diff --git a/lib/route53-functions b/lib/route53-functions index 08e9b553..75a0896b 100644 --- a/lib/route53-functions +++ b/lib/route53-functions @@ -42,23 +42,48 @@ hosted-zone-ns-records(){ # bash-my-aws.org. 300 IN NS ns-1464.awsdns-55.org. local inputs=$(skim-stdin "$@") - local hosted_zone_id=$(echo "$inputs" | awk '{print $1}') - [[ -z "$hosted_zone_id" ]] && __bma_usage "hosted-zone-id" && return 1 + local hosted_zone_ids=$(echo "$inputs") + [[ -z "$hosted_zone_ids" ]] && __bma_usage "hosted-zone-id" && return 1 local hosted_zone_name - hosted_zone_name=$( - aws route53 list-resource-record-sets \ - --hosted-zone-id "$hosted_zone_id" \ - --query "ResourceRecordSets[?Type=='NS'].Name" \ - --output text - ) + for hosted_zone_id in $hosted_zone_ids; do + hosted_zone_name=$( + aws route53 list-resource-record-sets \ + --hosted-zone-id "$hosted_zone_id" \ + --query "ResourceRecordSets[?Type=='NS'].Name" \ + --output text + ) - aws route53 list-resource-record-sets \ - --hosted-zone-id "$hosted_zone_id" \ - --output text \ - --query " - ResourceRecordSets[?Type=='NS'].ResourceRecords[].[ - '$hosted_zone_name 300 IN NS', - Value - ]" + aws route53 list-resource-record-sets \ + --hosted-zone-id "$hosted_zone_id" \ + --output text \ + --query " + ResourceRecordSets[?Type=='NS'].ResourceRecords[].[ + '$hosted_zone_name 300 IN NS', + Value + ]" + done +} + +hosted-zone-a-records(){ + + # Generate NS records for delegating domain to AWS + # + # $ hosted-zone-a-records bash-my-aws.org + # + # $ hosted-zones | hosted-zone-a-records + + local inputs=$(skim-stdin "$@") + local hosted_zone_ids=$(echo "$inputs") + [[ -z "$hosted_zone_ids" ]] && __bma_usage "hosted-zone-id" && return 1 + + local hosted_zone_name + for hosted_zone_id in $hosted_zone_ids; do + + aws route53 list-resource-record-sets \ + --hosted-zone-id "$hosted_zone_id" \ + --output text \ + --query "ResourceRecordSets[?Type=='A'].[Name,AliasTarget.DNSName]" | + columnise + done } diff --git a/lib/stack-functions b/lib/stack-functions index ffbff5af..d962b975 100644 --- a/lib/stack-functions +++ b/lib/stack-functions @@ -90,6 +90,7 @@ stacks() { --output text | grep -E -- "$filters" | LC_ALL=C sort -b -k 3 | + trim_date | columnise } @@ -557,22 +558,22 @@ stack-tag() { # XXX Handy but dangerous - updating a nested stack breaks things. Put in a guard. # XXX # XXX stack-tag-apply() { -# XXX +# XXX # XXX # Apply a stack tag -# XXX +# XXX # XXX local tag_key=$1 # XXX local tag_value=$2 # XXX shift 2 # XXX local usage_msg="tag-key tag-value stack [stack]" # XXX [[ -z "${tag_key}" ]] && __bma_usage $usage_msg && return 1 # XXX [[ -z "${tag_value}" ]] && __bma_usage $usage_msg && return 1 -# XXX +# XXX # XXX local stacks=$(skim-stdin "$@") # XXX [[ -z "${stacks}" && -t 0 ]] && __bma_usage $usage_msg && return 1 -# XXX +# XXX # XXX local stack # XXX for stack in $stacks; do -# XXX +# XXX # XXX # XXX deal with tagging service failing # XXX local tags=$(aws cloudformation describe-stacks \ # XXX --stack-name "$stack" \ @@ -580,7 +581,7 @@ stack-tag() { # XXX [{Key:'$tag_key', Value:'$tag_value'}], # XXX Stacks[].Tags[?Key != '$tag_key'][] # XXX ][]") -# XXX +# XXX # XXX local parameters=$(aws cloudformation describe-stacks \ # XXX --stack-name "$stack" \ # XXX --query ' @@ -588,11 +589,11 @@ stack-tag() { # XXX ParameterKey: ParameterKey, # XXX UsePreviousValue: `true` # XXX }') -# XXX +# XXX # XXX local capabilities='' # XXX local capabilities_value=$(_bma_stack_capabilities $stack) # XXX [[ -z "${capabilities_value}" ]] || capabilities="--capabilities ${capabilities_value}" -# XXX +# XXX # XXX $([[ -n $DRY_RUN ]] && echo echo) aws cloudformation update-stack \ # XXX --stack-name "$stack" \ # XXX --use-previous-template \ @@ -607,26 +608,26 @@ stack-tag() { # XXX Handy but dangerous - updating a nested stack breaks things. Put in a guard. # XXX # XXX stack-tag-delete() { -# XXX +# XXX # XXX # delete a stack tag -# XXX +# XXX # XXX local tag_key=$1 # XXX shift 1 # XXX [[ -z "${tag_key}" ]] && __bma_usage "tag-key stack [stack]" && return 1 -# XXX +# XXX # XXX local stacks=$(skim-stdin "$@") # XXX [[ -z "${stacks}" ]] && __bma_usage "tag-key stack [stack]" && return 1 -# XXX +# XXX # XXX local stack # XXX for stack in $stacks; do -# XXX +# XXX # XXX # XXX deal with tagging service failing # XXX local tags=$(aws cloudformation describe-stacks \ # XXX --stack-name "$stack" \ # XXX --query "[ # XXX Stacks[].Tags[?Key != '$tag_key'][] # XXX ][]") -# XXX +# XXX # XXX local parameters=$(aws cloudformation describe-stacks \ # XXX --stack-name "$stack" \ # XXX --query ' @@ -634,13 +635,13 @@ stack-tag() { # XXX ParameterKey: ParameterKey, # XXX UsePreviousValue: `true` # XXX }') -# XXX +# XXX # XXX aws cloudformation update-stack \ # XXX --stack-name "$stack" \ # XXX --use-previous-template \ # XXX --parameters "$parameters" \ # XXX --tags "$tags" -# XXX +# XXX # XXX done # XXX } @@ -681,19 +682,20 @@ stack-tail() { stack-template() { - # Return template of a stack - - local inputs=$(skim-stdin "$@") - local stack=$(_bma_stack_name_arg ${inputs}) + # Return template of each stack + local stacks=$(skim-stdin "$@") - [[ -z ${stack} ]] && __bma_usage "stack" && return 1 + [[ -z ${stacks} ]] && __bma_usage "stack [stack]" && return 1 + local stack + for stack in $stacks; do - aws cloudformation get-template \ - --stack-name "$stack" \ - --query TemplateBody | - jq --raw-output --sort-keys . | - sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' # remove trailing blank lines + aws cloudformation get-template \ + --stack-name "$stack" \ + --query TemplateBody | + jq --raw-output --sort-keys . | + sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' # remove trailing blank lines # sometimes appended by jq + done } stack-template-changeset-latest() { @@ -791,6 +793,88 @@ stack-validate() { fi } + +stack-detect-drift() { + + # Detect drift for provided stacks; and print coloured diff + local stacks=$(skim-stdin "$@") + [[ -z ${stacks} ]] && __bma_usage "stack [stack]" && return 1 + + for stack in $stacks; do + driftDetectionId=$(aws cloudformation detect-stack-drift \ + --stack-name "$stack" --output text) + DetectionStatus="" + while [[ "$DetectionStatus" != "DETECTION_COMPLETE" ]]; do + printf "." + DescribeDriftDetectionStatus=$(aws cloudformation describe-stack-drift-detection-status \ + --stack-drift-detection-id $driftDetectionId ) + DetectionStatus=$(echo $DescribeDriftDetectionStatus | jq --raw-output .DetectionStatus ) + StackDriftStatus=$(echo $DescribeDriftDetectionStatus | jq --raw-output .StackDriftStatus) + sleep 5; + done + if [[ "$StackDriftStatus" == "IN_SYNC" ]]; then + echo + echo "$stack is in sync" + else + echo "$stack has drifted" + stack-diff-drift $stack + fi + done + +} + +stack-describe-drift() { + + # List stack-tags applied to a stack + + local inputs=$(skim-stdin "$@") + local stack=$(_bma_stack_name_arg ${inputs}) + + [[ -z ${stack} ]] && __bma_usage "stack" && return 1 + + stackResourceDrifts=$( aws cloudformation describe-stack-resource-drifts \ + --stack-name "$stack" \ + --query 'StackResourceDrifts[*].{StackId:StackId,ExpectedProperties:ExpectedProperties,ActualProperties:ActualProperties}' ) +# jq -S <<< $stackResourceDrifts + ExpectedProperties=$(echo $stackResourceDrifts | jq --raw-output .[].ExpectedProperties) +# ExpectedPropertiesPretty=$(jq . -S --raw-output <<< "$ExpectedProperties") + ActualProperties=$(echo $stackResourceDrifts | jq --raw-output .[].ActualProperties) +# ActualPropertiesPretty=$(jq . -S --raw-output <<< "$ActualProperties") + echo + echo "Expected Properties:" + jq . <<< "$ExpectedProperties" + echo "Actual Properties:" + jq . <<< "$ActualProperties" + echo + +} + +stack-diff-drift() { + + # List stack-tags applied to a stack + + local inputs=$(skim-stdin "$@") + local stack=$(_bma_stack_name_arg ${inputs}) + + [[ -z ${stack} ]] && __bma_usage "stack" && return 1 + + stackResourceDrifts=$( aws cloudformation describe-stack-resource-drifts \ + --stack-name "$stack" \ + --query 'StackResourceDrifts[*].{StackId:StackId,ExpectedProperties:ExpectedProperties,ActualProperties:ActualProperties}' ) +# jq -S <<< $stackResourceDrifts + ExpectedProperties=$(echo $stackResourceDrifts | jq --raw-output .[].ExpectedProperties) + ExpectedPropertiesPretty=$(jq . -S --raw-output <<< "$ExpectedProperties") + ActualProperties=$(echo $stackResourceDrifts | jq --raw-output .[].ActualProperties) + ActualPropertiesPretty=$(jq . -S --raw-output <<< "$ActualProperties") + echo + # TODO : check whether diff is installed, otherwise just print $ExpectedPropertiesPretty and $ActualPropertiesPretty + echo "The diff ( Expected | Actual )" + diff -y --left-column --color=always <(printf %s "$ExpectedPropertiesPretty") <(printf %s "$ActualPropertiesPretty") + +} + + + stack-diff(){ # Compare live stack against local template (and optional params file) @@ -845,7 +929,7 @@ _bma_stack_diff_template() { elif command -v icdiff; then local DIFF_CMD=icdiff else - local DIFF_CMD=diff + local DIFF_CMD='diff --color=always' fi $DIFF_CMD -u \