Open-sourced my dtools CLI
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
name: aws
|
||||
help: AWS commands
|
||||
group: AWS
|
||||
expose: always
|
||||
dependencies:
|
||||
aws: Install the latest version following the instructions at 'https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html'
|
||||
|
||||
commands:
|
||||
- name: login
|
||||
help: |-
|
||||
Log in to AWS using SSO.
|
||||
This command will also set your 'AWS_PROFILE' and 'AWS_REGION' environment variables.
|
||||
It will also export temporary credentials to your environment with the 'AWS_ACCESS_KEY_ID' and 'AWS_SECRET_ACCESS_KEY' environment variables.
|
||||
|
||||
This command is essentially a shorthand for the following commands:
|
||||
dtools aws profile <PROFILE>
|
||||
dtools aws region <REGION>
|
||||
dtools aws login
|
||||
dtools aws export-sso-creds
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
examples:
|
||||
- dtools aws login -p prod -r us-east-1
|
||||
- |-
|
||||
# When the 'AWS_PROFILE' and 'AWS_REGION' environment variables are already
|
||||
# set
|
||||
dtools aws login
|
||||
|
||||
- name: console
|
||||
help: Open the AWS console in your default browser using the current AWS_REGION and AWS_PROFILE
|
||||
dependencies:
|
||||
wmctrl: Install with 'sudo apt-get install wmctrl'
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --service
|
||||
short: -s
|
||||
help: The AWS service to open the console to
|
||||
arg: service
|
||||
completions:
|
||||
- >-
|
||||
$(python -c $'import boto3\nfor service in boto3.Session().get_available_services(): print(service)' | grep -v 'codestar\|honeycode\|mobile\|worklink')
|
||||
|
||||
- name: shell
|
||||
help: Drop into an interactive AWS CLI shell with auto-completion
|
||||
|
||||
- name: profile
|
||||
help: Change AWS profile
|
||||
completions:
|
||||
- $(cat ~/.aws/config | awk '/\[profile*/ { print substr($2, 1, length($2)-1); }')
|
||||
args:
|
||||
- name: profile
|
||||
required: true
|
||||
help: The AWS profile to use, corresponding profiles in your ~/.aws/config
|
||||
validate: aws_profile_exists
|
||||
examples:
|
||||
- dtools aws profile prod
|
||||
|
||||
- name: region
|
||||
help: Change AWS region
|
||||
args:
|
||||
- name: region
|
||||
required: true
|
||||
help: The AWS region to use
|
||||
allowed:
|
||||
import: src/components/aws/allowed_regions.yml
|
||||
examples:
|
||||
- dtools aws region us-east-1
|
||||
|
||||
- name: toggle-auto-prompt
|
||||
help: Toggle the AWS CLI auto prompt
|
||||
|
||||
- name: export-sso-creds
|
||||
help: |-
|
||||
Exports SSO credentials to environment variables for use with AWS SDKs
|
||||
This includes all of the following variables:
|
||||
|
||||
AWS_ACCESS_KEY_ID
|
||||
AWS_SECRET_ACCESS_KEY
|
||||
AWS_SESSION_TOKEN
|
||||
AWS_CREDENTIAL_EXPIRATION
|
||||
AWS_REGION
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
|
||||
- name: generate-sso-profiles
|
||||
help: |-
|
||||
Fetch all AWS accounts via AWS SSO, and generate the profiles for CLI connectivity.
|
||||
|
||||
In the event that the script fails automation when selecting an account to use for the basic setup,
|
||||
you can manually perform this first step by running 'aws configure sso', use 'https://d-123456789ab.awsapps.com/start'
|
||||
as the SSO Start URL, and use any account with any settings. Then you can run this command again for it
|
||||
to work properly.
|
||||
|
||||
dependencies:
|
||||
jq: Install with 'brew install jq'
|
||||
flags:
|
||||
- long: --backup
|
||||
help: Create a backup of the previous AWS config
|
||||
- long: --default-cli-region
|
||||
short: -d
|
||||
arg: default-cli-region
|
||||
help: |-
|
||||
The default CLI region for each profile.
|
||||
Defaults to using the same region as the provided SSO region
|
||||
allowed:
|
||||
import: src/components/aws/allowed_regions.yml
|
||||
- long: --sso-region
|
||||
short: -r
|
||||
arg: sso-region
|
||||
required: true
|
||||
help: The region for SSO accounts
|
||||
allowed:
|
||||
import: src/components/aws/allowed_regions.yml
|
||||
- long: --sso-start-url
|
||||
short: -u
|
||||
arg: sso-start-url
|
||||
required: true
|
||||
help: The start URL for SSO authentication
|
||||
examples:
|
||||
- dtools aws generate-sso-profiles -u https://d-123456789ab.awsapps.com/start -r us-east-1
|
||||
|
||||
- import: src/commands/aws/ec2/ec2_commands.yml
|
||||
- import: src/commands/aws/ssm/ssm_commands.yml
|
||||
- import: src/commands/aws/secretsmanager/secretsmanager_commands.yml
|
||||
- import: src/commands/aws/logs/logs_commands.yml
|
||||
- import: src/commands/aws/rds/rds_commands.yml
|
||||
@@ -0,0 +1,438 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare service="${args[--service]}"
|
||||
declare base_aws_url="https://console.aws.amazon.com"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
if ! [[ -f /usr/local/bin/aws_console ]]; then
|
||||
cat <<EOF >> aws_console
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import json
|
||||
import webbrowser
|
||||
import urllib.parse
|
||||
import os
|
||||
import argparse
|
||||
from typing import Optional
|
||||
import time
|
||||
import pyautogui
|
||||
|
||||
import requests
|
||||
import boto3
|
||||
|
||||
|
||||
def get_logout_url(region: Optional[str] = None):
|
||||
urllib.parse.quote_plus(
|
||||
"https://aws.amazon.com/premiumsupport/knowledge-center/sign-out-account/?from_aws_sso_util_logout"
|
||||
)
|
||||
if not region or region == "us-east-1":
|
||||
return "https://signin.aws.amazon.com/oauth?Action=logout&redirect_uri="
|
||||
|
||||
if region == "us-gov-east-1":
|
||||
return "https://us-gov-east-1.signin.amazonaws-us-gov.com/oauth?Action=logout"
|
||||
|
||||
if region == "us-gov-west-1":
|
||||
return "https://signin.amazonaws-us-gov.com/oauth?Action=logout"
|
||||
|
||||
return f"https://{region}.signin.aws.amazon.com/oauth?Action=logout&redirect_uri="
|
||||
|
||||
|
||||
def get_federation_endpoint(region: Optional[str] = None):
|
||||
if not region or region == "us-east-1":
|
||||
return "https://signin.aws.amazon.com/federation"
|
||||
|
||||
if region == "us-gov-east-1":
|
||||
return "https://us-gov-east-1.signin.amazonaws-us-gov.com/federation"
|
||||
|
||||
if region == "us-gov-west-1":
|
||||
return "https://signin.amazonaws-us-gov.com/federation"
|
||||
|
||||
return f"https://{region}.signin.aws.amazon.com/federation"
|
||||
|
||||
|
||||
def get_destination_base_url(region: Optional[str] = None):
|
||||
if region and region.startswith("us-gov-"):
|
||||
return "https://console.amazonaws-us-gov.com"
|
||||
if region:
|
||||
return f"https://{region}.console.aws.amazon.com/"
|
||||
|
||||
return "https://console.aws.amazon.com/"
|
||||
|
||||
|
||||
def get_destination(
|
||||
path: Optional[str] = None,
|
||||
region: Optional[str] = None,
|
||||
override_region_in_destination: bool = False,
|
||||
):
|
||||
base = get_destination_base_url(region=region)
|
||||
|
||||
if path:
|
||||
stripped_path_parts = urllib.parse.urlsplit(path)[2:]
|
||||
path = urllib.parse.urlunsplit(("", "") + stripped_path_parts)
|
||||
url = urllib.parse.urljoin(base, path)
|
||||
else:
|
||||
url = base
|
||||
|
||||
if not region:
|
||||
return url
|
||||
|
||||
parts = list(urllib.parse.urlsplit(url))
|
||||
query_params = urllib.parse.parse_qsl(parts[3])
|
||||
if override_region_in_destination:
|
||||
query_params = [(k, v) for k, v in query_params if k != "region"]
|
||||
query_params.append(("region", region))
|
||||
elif not any(k == "region" for k, _ in query_params):
|
||||
query_params.append(("region", region))
|
||||
query_str = urllib.parse.urlencode(query_params)
|
||||
parts[3] = query_str
|
||||
|
||||
url = urllib.parse.urlunsplit(parts)
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def DurationType(value):
|
||||
value = int(value)
|
||||
if 15 < value < 720:
|
||||
raise ValueError("Duration must be between 15 and 720 minutes (inclusive)")
|
||||
return value
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Launch the AWS console")
|
||||
|
||||
parser.add_argument("--profile", metavar="PROFILE_NAME", help="A config profile to use")
|
||||
parser.add_argument("--region", metavar="REGION", help="The AWS region")
|
||||
parser.add_argument(
|
||||
"--destination",
|
||||
dest="destination_path",
|
||||
metavar="PATH",
|
||||
help="Console URL path to go to",
|
||||
)
|
||||
|
||||
override_region_group = parser.add_mutually_exclusive_group()
|
||||
override_region_group.add_argument("--override-region-in-destination", action="store_true")
|
||||
override_region_group.add_argument(
|
||||
"--keep-region-in-destination",
|
||||
dest="override_region_in_destination",
|
||||
action="store_false",
|
||||
)
|
||||
|
||||
open_group = parser.add_mutually_exclusive_group()
|
||||
open_group.add_argument(
|
||||
"--open",
|
||||
dest="open_url",
|
||||
action="store_true",
|
||||
default=None,
|
||||
help="Open the login URL in a browser (the default)",
|
||||
)
|
||||
open_group.add_argument(
|
||||
"--no-open",
|
||||
dest="open_url",
|
||||
action="store_false",
|
||||
help="Do not open the login URL",
|
||||
)
|
||||
|
||||
print_group = parser.add_mutually_exclusive_group()
|
||||
print_group.add_argument(
|
||||
"--print",
|
||||
dest="print_url",
|
||||
action="store_true",
|
||||
default=None,
|
||||
help="Print the login URL",
|
||||
)
|
||||
print_group.add_argument(
|
||||
"--no-print",
|
||||
dest="print_url",
|
||||
action="store_false",
|
||||
help="Do not print the login URL",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--duration",
|
||||
metavar="MINUTES",
|
||||
type=DurationType,
|
||||
help="The session duration in minutes",
|
||||
)
|
||||
|
||||
logout_first_group = parser.add_mutually_exclusive_group()
|
||||
logout_first_group.add_argument(
|
||||
"--logout-first",
|
||||
"-l",
|
||||
action="store_true",
|
||||
default=True,
|
||||
help="Open a logout page first",
|
||||
)
|
||||
logout_first_group.add_argument(
|
||||
"--no-logout-first",
|
||||
dest="logout_first",
|
||||
action="store_false",
|
||||
help="Do not open a logout page first",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.open_url is None:
|
||||
args.open_url = True
|
||||
|
||||
logout_first_from_env = False
|
||||
if args.logout_first is None:
|
||||
args.logout_first = os.environ.get("AWS_CONSOLE_LOGOUT_FIRST", "").lower() in [
|
||||
"true",
|
||||
"1",
|
||||
]
|
||||
logout_first_from_env = True
|
||||
|
||||
if args.logout_first and not args.open_url:
|
||||
if logout_first_from_env:
|
||||
logout_first_value = os.environ["AWS_CONSOLE_LOGOUT_FIRST"]
|
||||
raise parser.exit(f"AWS_CONSOLE_LOGOUT_FIRST={logout_first_value} requires --open")
|
||||
else:
|
||||
raise parser.exit("--logout-first requires --open")
|
||||
|
||||
session = boto3.Session(profile_name=args.profile)
|
||||
|
||||
if not args.region:
|
||||
args.region = session.region_name or os.environ.get("AWS_CONSOLE_DEFAULT_REGION")
|
||||
if not args.destination_path:
|
||||
args.destination_path = session._session.get_scoped_config().get("web_console_destination") or os.environ.get(
|
||||
"AWS_CONSOLE_DEFAULT_DESTINATION"
|
||||
)
|
||||
|
||||
credentials = session.get_credentials()
|
||||
if not credentials:
|
||||
parser.exit("Could not find credentials")
|
||||
|
||||
federation_endpoint = get_federation_endpoint(region=args.region)
|
||||
issuer = os.environ.get("AWS_CONSOLE_DEFAULT_ISSUER")
|
||||
destination = get_destination(
|
||||
path=args.destination_path,
|
||||
region=args.region,
|
||||
override_region_in_destination=args.override_region_in_destination,
|
||||
)
|
||||
|
||||
launch_console(
|
||||
session=session,
|
||||
federation_endpoint=federation_endpoint,
|
||||
destination=destination,
|
||||
region=args.region,
|
||||
open_url=args.open_url,
|
||||
print_url=args.print_url,
|
||||
duration=args.duration,
|
||||
logout_first=args.logout_first,
|
||||
issuer=issuer,
|
||||
)
|
||||
|
||||
|
||||
def launch_console(
|
||||
session: boto3.Session,
|
||||
federation_endpoint: str,
|
||||
destination: str,
|
||||
region: Optional[str] = None,
|
||||
open_url: Optional[bool] = None,
|
||||
print_url: Optional[bool] = None,
|
||||
duration: Optional[int] = None,
|
||||
logout_first: Optional[bool] = None,
|
||||
issuer: Optional[str] = None,
|
||||
):
|
||||
if not issuer:
|
||||
issuer = "aws_console_launcher.py"
|
||||
|
||||
read_only_credentials = session.get_credentials().get_frozen_credentials()
|
||||
|
||||
session_data = {
|
||||
"sessionId": read_only_credentials.access_key,
|
||||
"sessionKey": read_only_credentials.secret_key,
|
||||
"sessionToken": read_only_credentials.token,
|
||||
}
|
||||
|
||||
get_signin_token_payload = {
|
||||
"Action": "getSigninToken",
|
||||
"Session": json.dumps(session_data),
|
||||
}
|
||||
if duration is not None:
|
||||
get_signin_token_payload["SessionDuration"] = duration * 60
|
||||
|
||||
response = requests.post(federation_endpoint, data=get_signin_token_payload)
|
||||
|
||||
if response.status_code != 200:
|
||||
print("Could not get signin token", file=sys.stderr)
|
||||
print(response.status_code + "\n" + response.text, file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
token = response.json()["SigninToken"]
|
||||
|
||||
get_login_url_params = {
|
||||
"Action": "login",
|
||||
"Issuer": issuer,
|
||||
"Destination": destination,
|
||||
"SigninToken": token,
|
||||
}
|
||||
|
||||
request = requests.Request(method="GET", url=federation_endpoint, params=get_login_url_params)
|
||||
|
||||
prepared_request = request.prepare()
|
||||
|
||||
login_url = prepared_request.url
|
||||
|
||||
if print_url:
|
||||
print(login_url)
|
||||
|
||||
if open_url:
|
||||
if logout_first:
|
||||
logout_url = get_logout_url(region=region)
|
||||
webbrowser.open(logout_url, autoraise=False)
|
||||
time.sleep(1)
|
||||
os.system('wmctrl -a "Manage AWS Resources"')
|
||||
pyautogui.hotkey("ctrl", "w")
|
||||
|
||||
webbrowser.open(login_url)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
EOF
|
||||
|
||||
chmod +x aws_console
|
||||
sudo mv aws_console /usr/local/bin/
|
||||
fi
|
||||
|
||||
declare -A service_aliases=(
|
||||
[accessanalyzer]="access-analyzer"
|
||||
[alexaforbusiness]="a4b"
|
||||
[apigatewaymanagementapi]="apigateway"
|
||||
[apigatewayv2]="apigateway"
|
||||
[appconfig]="systems-manager/appconfig"
|
||||
[application-autoscaling]="awsautoscaling"
|
||||
[application-insights]="cloudwatch/home?#settings:AppInsightsSettings"
|
||||
[appstream]="appstream2"
|
||||
[autoscaling]="ec2/home#AutoScalingGroups:"
|
||||
[autoscaling-plans]="awsautoscaling/home#dashboard"
|
||||
[budgets]="billing/home#/budgets"
|
||||
[ce]="costmanagement/home#/cost-explorer"
|
||||
[chime]="chime-sdk"
|
||||
[clouddirectory]="directoryservicev2/home#!/cloud-directories"
|
||||
[cloudhsmv2]="cloudhsm"
|
||||
[cloudsearchdomain]="cloudsearch"
|
||||
[codeartifact]="codesuite/codeartifact"
|
||||
[codeguru-reviewer]="codeguru/reviewer"
|
||||
[codeguruprofiler]="codeguru/profiler"
|
||||
[cognito-identity]="iamv2/home#/identity_providers"
|
||||
[cognito-idp]="cognito/v2/idp"
|
||||
[cognito-sync]="appsync"
|
||||
[connectparticipant]="connect"
|
||||
[cur]="billing/home#/reports"
|
||||
[dax]="dynamodbv2/home#dax-clusters"
|
||||
[directconnect]="directconnect/v2/home"
|
||||
[dlm]="ec2/home#Lifecycle"
|
||||
[dms]="dms/v2"
|
||||
[ds]="directoryservicev2"
|
||||
[dynamodbstreams]="dynamodbv2"
|
||||
[ebs]="ec2/home#Volumes:"
|
||||
[ec2-instance-connect]="ec2/home#Instances:"
|
||||
[elastic-inference]="sagemaker"
|
||||
[elb]="ec2/home#LoadBalancers:"
|
||||
[elbv2]="ec2/home#LoadBalancers:"
|
||||
[es]="aos/home"
|
||||
[fms]="wafv2/fmsv2/home"
|
||||
[forecastquery]="forecast"
|
||||
[glacier]="glacier/home"
|
||||
[globalaccelerator]="globalaccelerattor/home"
|
||||
[identitystore]="singlesignon"
|
||||
[iot-data]="iot"
|
||||
[iot-jobs-data]="iot/home#/jobhub"
|
||||
[iot1click-devices]="iot/home#/thinghub"
|
||||
[iot1click-projects]="iot"
|
||||
[iotevents-data]="iotevents/home#/input"
|
||||
[iotsecuretunneling]="iot/home#/tunnelhub"
|
||||
[iotthingsgraph]="iot/home#/thinghub"
|
||||
[kafka]="msk"
|
||||
[kinesis-video-archived-media]="kinesisvideo/home"
|
||||
[kinesis-video-media]="kinesisvideo/home"
|
||||
[kinesis-video-signaling]="kinesisvideo/home#/signalingChannels"
|
||||
[kinesisanalyticsv2]="flink"
|
||||
[kinesisvideo]="kinesisvideo/home"
|
||||
[lex-models]="lexv2/home#bots"
|
||||
[lex-runtime]="lexv2/home#bots"
|
||||
[lightsail]="ls"
|
||||
[logs]="cloudwatch/home#logsV2:"
|
||||
[macie2]="macie"
|
||||
[marketplace-catalog]="marketplace/home#/search!mpSearch/search"
|
||||
[marketplace-entitlement]="marketplace"
|
||||
[marketplacecommerceanalytics]="marketplace/home#/vendor-insights"
|
||||
[mediapackage-vod]="mediapackagevod"
|
||||
[mediastore-data]="mediastore"
|
||||
[meteringmarketplace]="marketplace"
|
||||
[mgh]="migrationhub"
|
||||
[migrationhub-config]="migrationhub"
|
||||
[mq]="amazon-mq"
|
||||
[networkmanager]="networkmanager/home"
|
||||
[opsworkscm]="opsworks"
|
||||
[personalize]="personalize/home"
|
||||
[personalize-events]="personalize/home"
|
||||
[personalize-runtime]="personalize/home"
|
||||
[pi]="rds/home#performance-insights"
|
||||
[pinpoint]="pinpointv2"
|
||||
[pinpoint-email]="pinpoint/home#/email-account-settings/overview"
|
||||
[pinpoint-sms-voice]="pinpoint"
|
||||
[qldb-session]="qldb"
|
||||
[ram]="ram/home"
|
||||
[rds-data]="rds/home#query-editor:"
|
||||
[redshift-data]="redshiftv2/home#/query-editor:"
|
||||
[resourcegroupstaggingapi]="resource-groups"
|
||||
[route53domains]="route53/domains"
|
||||
[s3control]="s3"
|
||||
[sagemaker-a2i-runtime]="sagemaker/groundtruth#/a2i"
|
||||
[sagemaker-runtime]="sagemaker"
|
||||
[savingsplans]="costmanagement/home#/savings-plans/overview"
|
||||
[schemas]="events/home#/schemas"
|
||||
[sdb]="simpledb"
|
||||
[service-quotas]="servicequotas"
|
||||
[servicediscovery]="cloudmap"
|
||||
[shield]="wafv2/shieldv2"
|
||||
[sms]="mgn/home"
|
||||
[snowball]="snowfamily"
|
||||
[ssm]="systems-manager"
|
||||
[sso]="singlesignon"
|
||||
[sso-admin]="singlesignon"
|
||||
[sso-oidc]="singlesignon"
|
||||
[stepfunctions]="states"
|
||||
[sts]="iam"
|
||||
[swf]="swf/v2"
|
||||
[translate]="translate/home"
|
||||
[waf]="wafv2/homev2"
|
||||
[waf-regional]="wafv2/homev2"
|
||||
[wafv2]="wafv2/homev2"
|
||||
[workdocs]="zocalo"
|
||||
[workmailmessageflow]="workmail"
|
||||
[xray]="xray/home"
|
||||
)
|
||||
|
||||
case "$service" in
|
||||
"pricing")
|
||||
firefox "https://calculator.aws" > /dev/null 2>&1
|
||||
exit
|
||||
;;
|
||||
"mturk")
|
||||
firefox "https://mturk.com" > /dev/null 2>&1
|
||||
exit
|
||||
;;
|
||||
"quicksight")
|
||||
firefox "quicksight.aws.amazon.com" > /dev/null 2>&1
|
||||
exit
|
||||
;;
|
||||
*)
|
||||
if [[ -v service_aliases["$service"] ]]; then
|
||||
service_url="${base_aws_url}/${service_aliases[$service]}"
|
||||
else
|
||||
service_url="${base_aws_url}/${service}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
aws_console --profile "$aws_profile" --region "$aws_region" --destination "$service_url"
|
||||
@@ -0,0 +1,28 @@
|
||||
name: ec2
|
||||
help: EC2 commands
|
||||
group: EC2
|
||||
expose: always
|
||||
dependencies:
|
||||
aws: Install the latest version following the instructions at 'https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html'
|
||||
jq: Install using 'brew install jq'
|
||||
|
||||
commands:
|
||||
- name: list-instances
|
||||
help: List all EC2 instances in the account
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --detailed
|
||||
help: Output the list of all instances in the full detailed format
|
||||
conflicts: [--filter]
|
||||
- long: --filter
|
||||
short: -f
|
||||
arg: filter
|
||||
help: Filter the output to only show the specified information
|
||||
repeatable: true
|
||||
unique: true
|
||||
allowed:
|
||||
import: src/components/aws/ec2/allowed_list_instance_filters.yml
|
||||
conflicts: [--detailed]
|
||||
@@ -0,0 +1,49 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare detailed_format="${args[--detailed]}"
|
||||
eval "filters=(${args[--filter]:-})"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
spinny-start
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
declare instances=$(aws ec2 describe-instances --profile "$aws_profile" --region "$aws_region")
|
||||
spinny-stop
|
||||
|
||||
# Must be ordered by non-nested fields first
|
||||
declare -A instance_field_mappings=(
|
||||
[instance-id]='InstanceId'
|
||||
[instance-type]='InstanceType'
|
||||
[private-dns-name]='PrivateDnsName'
|
||||
[private-ip-address]='PrivateIpAddress'
|
||||
[public-dns-name]='PublicDnsName'
|
||||
[subnet-id]='SubnetId'
|
||||
[vpc-id]='VpcId'
|
||||
[tags]='Tags'
|
||||
[launch-time]='LaunchTime'
|
||||
[architecture]='Architecture'
|
||||
[instance-profile]='IamInstanceProfile'
|
||||
[security-groups]='SecurityGroups'
|
||||
[availability-zone]='"AvailabilityZone": .Placement.AvailabilityZone'
|
||||
[state]='"State": .State.Name'
|
||||
[os]='"OS": .PlatformDetails'
|
||||
)
|
||||
|
||||
if [[ $detailed_format == 1 ]]; then
|
||||
jq . <<< "$instances"
|
||||
elif [[ -v filters[@] ]]; then
|
||||
declare object_def=""
|
||||
|
||||
for filter_name in "${!instance_field_mappings[@]}"; do
|
||||
# shellcheck disable=SC2154
|
||||
if printf '%s\0' "${filters[@]}" | grep -Fxqz -- "$filter_name"; then
|
||||
object_def+="${instance_field_mappings[$filter_name]}, "
|
||||
fi
|
||||
done
|
||||
|
||||
jq '.Reservations[].Instances[] | { '"$object_def"' }' <<< "$instances"
|
||||
else
|
||||
jq '.Reservations[].Instances[] | pick(.InstanceId, .PrivateDnsName, .PrivateIpAdress, .PublicDnsName, .SubnetId, .VpcId, .Tags)' <<< "$instances"
|
||||
fi
|
||||
@@ -0,0 +1,7 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
declare aws_region="$(get-aws-region)"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
bash -c "eval \"\$(aws --profile $aws_profile --region $aws_region configure export-credentials --format env)\"; export AWS_REGION=$aws_region; exec bash"
|
||||
@@ -0,0 +1,123 @@
|
||||
# shellcheck disable=SC2154
|
||||
declare aws_region="${args[--default-cli-region]}"
|
||||
declare sso_region="${args[--sso-region]}"
|
||||
declare sso_start_url="${args[--sso-start-url]}"
|
||||
declare backup="${args[--backup]}"
|
||||
|
||||
set -e
|
||||
|
||||
if [[ -z $aws_region ]]; then
|
||||
aws_region="$sso_region"
|
||||
fi
|
||||
|
||||
export AWS_REGION=$aws_region
|
||||
|
||||
write-profile-to-config() {
|
||||
profileName=$1
|
||||
ssoStartUrl=$2
|
||||
ssoRegion=$3
|
||||
ssoAccountId=$4
|
||||
ssoRoleName=$5
|
||||
defaultRegion=$6
|
||||
|
||||
blue_bold "Creating profile $profileName"
|
||||
|
||||
cat <<-EOF >> "$HOME"/.aws/config
|
||||
[profile $profileName]
|
||||
sso_start_url = $ssoStartUrl
|
||||
sso_region = $ssoRegion
|
||||
sso_account_id = $ssoAccountId
|
||||
sso_role_name = $ssoRoleName
|
||||
region = $defaultRegion
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ $backup == 1 ]]; then
|
||||
yellow "Backing up old AWS config"
|
||||
mv "$HOME"/.aws/config "$HOME"/.aws/config.bak
|
||||
fi
|
||||
|
||||
login() {
|
||||
ssoLoggedIn=$(find "$HOME/.aws/sso/cache" -type f ! -name "botocore*" -exec jq -r '.accessToken | select(. != null)' {} \; | wc -l)
|
||||
if [[ $ssoLoggedIn == 0 || ! -f "$HOME"/.aws/config ]]; then
|
||||
yellow_bold "You must first be logged into AWS with at least one profile. Logging in now..."
|
||||
[[ -f "$HOME"/.aws/config ]] || touch "$HOME"/.aws/config
|
||||
|
||||
export AWS_PROFILE=''
|
||||
export AWS_REGION=''
|
||||
/usr/bin/expect<<-EOF
|
||||
set force_conservative 1
|
||||
set timeout 120
|
||||
match_max 100000
|
||||
spawn aws configure sso
|
||||
expect "SSO session name (Recommended):"
|
||||
send -- "session\r"
|
||||
expect "SSO start URL"
|
||||
send -- "$sso_start_url\\r"
|
||||
expect "SSO region"
|
||||
send -- "$sso_region\r"
|
||||
expect {
|
||||
"SSO registration scopes" {
|
||||
send "sso:account:access\\r"
|
||||
exp_continue
|
||||
}
|
||||
-re {(.*)accounts available to you(.*)} {
|
||||
send "\\r"
|
||||
exp_continue
|
||||
}
|
||||
-re {(.*)roles available to you(.*)} {
|
||||
send "\\r"
|
||||
exp_continue
|
||||
}
|
||||
"CLI default client Region"
|
||||
}
|
||||
send "\r\r\r\r"
|
||||
expect eof
|
||||
EOF
|
||||
elif ! (aws sts get-caller-identity > /dev/null 2>&1); then
|
||||
red_bold "You must be logged into AWS before running this script."
|
||||
yellow "Logging in via SSO. Follow the steps in the opened browser to log in."
|
||||
|
||||
profiles=$(awk '/\[profile*/ { print substr($2, 1, length($2)-1); }' ~/.aws/config | tail -1)
|
||||
|
||||
if ! aws sso login --profile "${profiles[0]}"; then
|
||||
red_bold "Unable to login. Please try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
green "Logged in!"
|
||||
fi
|
||||
|
||||
blue "Fetching SSO access token"
|
||||
profiles=$(awk '/\[profile*/ { print substr($2, 1, length($2)-1); }' ~/.aws/config | tail -1)
|
||||
# shellcheck disable=SC2227
|
||||
ACCESS_TOKEN=$(find "$HOME/.aws/sso/cache" -type f ! -name 'botocore*' -exec jq -r '.accessToken | select(. != null)' {} 2>/dev/null \; | tail -1)
|
||||
}
|
||||
|
||||
login
|
||||
|
||||
if ! (aws sso list-accounts --profile "${profiles[0]}" --region "$aws_region" --access-token "$ACCESS_TOKEN" --output json > /dev/null 2>&1); then
|
||||
red "Unable to use existing SSO access token. Wiping tokens and generating new tokens..."
|
||||
rm "$HOME"/.aws/sso/cache/*.json
|
||||
login
|
||||
fi
|
||||
|
||||
aws sso list-accounts --profile "${profiles[0]}" --region "$aws_region" --access-token "$ACCESS_TOKEN" --output json | jq '.accountList[]' -rc | while read -r account; do
|
||||
declare accountId
|
||||
declare accountName
|
||||
accountId="$(echo "$account" | jq -rc '.accountId')"
|
||||
accountName="$(echo "$account" | jq -rc '.accountName | ascii_downcase | gsub(" "; "-")')"
|
||||
|
||||
aws sso list-account-roles --profile "${profiles[0]}" --region "$aws_region" --access-token "$ACCESS_TOKEN" --output json --account-id "$accountId" | jq '.roleList[].roleName' -rc | while read -r roleName; do
|
||||
declare profileName
|
||||
profileName="$accountName-$roleName"
|
||||
|
||||
if ! (grep -q "$profileName" ~/.aws/config); then
|
||||
blue "Creating profiles for account $accountName"
|
||||
write-profile-to-config "$accountName-$roleName" "$sso_start_url" "$sso_region" "$accountId" "$roleName" "$aws_region"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
green_bold "Successfully generated profiles from AWS SSO!"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
declare aws_region="$(get-aws-region)"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
if ( grep "AWS_PROFILE" ~/.bashrc > /dev/null 2>&1 ); then
|
||||
sed -i "/^AWS_PROFILE=/c\export AWS_PROFILE=$aws_profile" ~/.bashrc
|
||||
fi
|
||||
|
||||
if ( grep "AWS_REGION" ~/.bashrc > /dev/null 2>&1 ); then
|
||||
sed -i "/^AWS_REGION=/c\export AWS_REGION=$aws_region" ~/.bashrc
|
||||
fi
|
||||
|
||||
bash -c "export AWS_PROFILE=$aws_profile; export AWS_REGION=$aws_region; eval \"\$(aws configure export-credentials --format env --profile $aws_profile)\"; exec bash"
|
||||
@@ -0,0 +1,15 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare detailed_format="${args[--detailed]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
declare log_groups=$(aws logs describe-log-groups --profile "$aws_profile" --region "$aws_region")
|
||||
|
||||
if [[ $detailed_format == 1 ]]; then
|
||||
jq . <<< "$log_groups"
|
||||
else
|
||||
jq -r '.logGroups[].logGroupName' <<< "$log_groups"
|
||||
fi
|
||||
@@ -0,0 +1,77 @@
|
||||
name: logs
|
||||
help: AWS Logs commands
|
||||
group: Logs
|
||||
expose: always
|
||||
dependencies:
|
||||
aws: Install the latest version following the instructions at 'https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html'
|
||||
lnav: Install with 'brew install lnav'
|
||||
unbuffer: Install with 'brew install expect'
|
||||
|
||||
commands:
|
||||
- name: list-log-groups
|
||||
help: List all of the log groups in CloudWatch
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --detailed
|
||||
help: Output the list of all CloudWatch log groups in the full detailed format
|
||||
|
||||
- name: tail-log-group
|
||||
help: Tails the specified CloudWatch log group
|
||||
dependencies:
|
||||
lnav: Install with 'brew install lnav'
|
||||
unbuffer: Install with 'brew install expect'
|
||||
args:
|
||||
- name: log-group
|
||||
required: true
|
||||
help: The name of the log group to tail
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --since
|
||||
short: -s
|
||||
arg: since
|
||||
default: 10m
|
||||
help: The time to start tailing the log group from
|
||||
validate: relative_since_time_format
|
||||
completions:
|
||||
- $(for e in s m h d w; do echo "${2//[!0-9]/}${e}"; done)
|
||||
- long: --verbose
|
||||
short: -v
|
||||
help: Show verbose log output
|
||||
- long: --stdout
|
||||
help: Show the log output in stdout
|
||||
examples:
|
||||
- dtools aws tail-log-group /aws/lambda/test-lambda-1
|
||||
|
||||
- name: query-log-groups
|
||||
help: Query one or more log groups with the given query string
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
args:
|
||||
- name: query
|
||||
help: The query string to query the log groups for
|
||||
default: fields @timestamp, @message | sort @timestamp desc
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --log-group-name
|
||||
short: -l
|
||||
help: The names of a log group to query for
|
||||
repeatable: true
|
||||
arg: log_group_name
|
||||
required: true
|
||||
- long: --start-time
|
||||
help: The start time for the query (ISO 8601)
|
||||
arg: start_time
|
||||
required: true
|
||||
- long: --end-time
|
||||
help: The end time for the query (ISO 8601)
|
||||
arg: end_time
|
||||
required: true
|
||||
examples:
|
||||
- dtools aws logs query-log-groups 'correlationId' --start-time '2025-03-18T15:00:00Z' --end-time '2025-03-18T16:00:00Z' --log-group-name caerus-api-log-group -l /aws/lambda/revisit-prod-revisit-core-historical-schedules-s3-writer-lambda
|
||||
@@ -0,0 +1,43 @@
|
||||
# shellcheck disable=SC2155
|
||||
export aws_region="$(get-aws-region)"
|
||||
# shellcheck disable=SC2155
|
||||
export aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
export query="${args[query]}"
|
||||
# shellcheck disable=SC2154
|
||||
export start_time="${args[--start-time]}"
|
||||
# shellcheck disable=SC2154
|
||||
export end_time="${args[--end-time]}"
|
||||
eval "log_group_names=(${args[--log-group-name]})"
|
||||
export log_file=$(mktemp)
|
||||
trap "rm -f $log_file" EXIT
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
write-logs() {
|
||||
log_group="$1"
|
||||
query_id="$(aws logs start-query \
|
||||
--log-group-names "$log_group" \
|
||||
--start-time "$(date -d "$start_time" +"%s%3N")" \
|
||||
--end-time "$(date -d "$end_time" +"%s%3N")" \
|
||||
--query-string "$query" \
|
||||
--profile "$aws_profile" \
|
||||
--region "$aws_region" \
|
||||
--output json | jq -r '.queryId // empty')"
|
||||
|
||||
if [[ -z $query_id ]]; then
|
||||
red "Unable to start query for log group: '$log_group'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
until [[ "$(aws logs get-query-results --query-id "$query_id" --profile "$aws_profile" --region "$aws_region" --query status --output text)" == "Complete" ]]; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
aws logs get-query-results --query-id "$query_id" --profile "$aws_profile" --region "$aws_region" | tr -d '\000-\037' | jq -r --arg log_group "$log_group" '.results[] | { "timestamp": (.[] | select(.field == "@timestamp") | .value), "message": (.[] | select(.field == "@message") | .value), "logGroup": $log_group }' >> "$log_file"
|
||||
}
|
||||
export -f write-logs
|
||||
|
||||
parallel -j8 write-logs {} ::: ${log_group_names[*]}
|
||||
|
||||
jq -rs '. | sort_by(.timestamp) | map("\(.timestamp) \(.logGroup) \(.message)")[]' "$log_file" | sed '/^$/d'
|
||||
@@ -0,0 +1,31 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare temp_log_file="$(mktemp)"
|
||||
set -e
|
||||
# shellcheck disable=SC2064
|
||||
# 'kill -- -$$' also kills the entire process group whose ID is $$
|
||||
# So this means that this will also kill all subprocesses started by
|
||||
# this script
|
||||
trap "rm -f $temp_log_file && kill -- -$$" EXIT
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
# shellcheck disable=SC2154
|
||||
unbuffer aws --profile "$aws_profile" --region "$aws_region" logs tail "${args[log-group]}" --follow --format short --no-cli-auto-prompt --since "${args[--since]}" >> "$temp_log_file" &
|
||||
|
||||
if [[ ${args[--verbose]} == 1 ]]; then
|
||||
if [[ ${args[--stdout]} == 1 ]]; then
|
||||
tail -f "$temp_log_file"
|
||||
else
|
||||
lnav "$temp_log_file"
|
||||
fi
|
||||
elif [[ ${args[--stdout]} == 1 ]]; then
|
||||
tail -f "$temp_log_file" |\
|
||||
awk '{$1=""; gsub(/^[ \t]+/, "", $0); if ($0 !~ /^END|^REPORT|^START/) { print }}'
|
||||
else
|
||||
tail -f "$temp_log_file" |\
|
||||
awk '{$1=""; gsub(/^[ \t]+/, "", $0); if ($0 !~ /^END|^REPORT|^START/) { print }}' |\
|
||||
lnav
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
set-aws-profile() {
|
||||
if ( grep -q "AWS_PROFILE" ~/.bashrc ); then
|
||||
sed -i "/^AWS_PROFILE=/c\export AWS_PROFILE=$1" ~/.bashrc
|
||||
fi
|
||||
|
||||
bash -c "export AWS_PROFILE=$1; exec bash"
|
||||
}
|
||||
|
||||
declare profile
|
||||
# shellcheck disable=SC2154
|
||||
set-aws-profile "${args[profile]}"
|
||||
@@ -0,0 +1,16 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare db_instance="${args[db_instance]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
spinny-start
|
||||
aws --profile "$aws_profile" \
|
||||
--region "$aws_region" \
|
||||
rds describe-db-instances \
|
||||
--query DBInstances[] |\
|
||||
jq -r '.[] | select(.DBInstanceIdentifier == "'"$db_instance"'") | .Endpoint | {"address": .Address, "port": .Port}'
|
||||
spinny-stop
|
||||
@@ -0,0 +1,14 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
spinny-start
|
||||
aws --profile "$aws_profile" \
|
||||
--region "$aws_region" \
|
||||
rds describe-db-instances \
|
||||
--query 'DBInstances[].DBInstanceIdentifier' \
|
||||
--output text
|
||||
spinny-stop
|
||||
@@ -0,0 +1,28 @@
|
||||
name: rds
|
||||
help: RDS commands
|
||||
group: RDS
|
||||
expose: always
|
||||
dependencies:
|
||||
aws: Install the latest version following the instructions at 'https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html'
|
||||
jq: Install with 'brew install jq'
|
||||
|
||||
commands:
|
||||
- name: list-db-instances
|
||||
help: List all RDS DB instances for the given account by their name
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
|
||||
- name: fetch-db-connection-details
|
||||
help: Fetch the connection details for the given RDS DB instance
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
args:
|
||||
- name: db_instance
|
||||
required: true
|
||||
help: The RDS DB instance name
|
||||
@@ -0,0 +1,8 @@
|
||||
declare region
|
||||
# shellcheck disable=SC2154
|
||||
region="${args[region]}"
|
||||
if ( grep -q "AWS_REGION" ~/.bashrc ); then
|
||||
sed -i "/^AWS_REGION=/c\export AWS_REGION=$region" ~/.bashrc
|
||||
fi
|
||||
|
||||
bash -c "export AWS_REGION=$region; exec bash"
|
||||
@@ -0,0 +1,10 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare secret_name="${args[--name]}"
|
||||
declare secret_string="${args[--secret-string]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
aws secretsmanager create-secret --name "$secret_name" --secret-string "$secret_string" --profile "$aws_profile" --region "$aws_region"
|
||||
@@ -0,0 +1,16 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare detailed_format="${args[--detailed]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
declare secrets=$(aws secretsmanager list-secrets --profile "$aws_profile" --region "$aws_region")
|
||||
|
||||
if [[ $detailed_format == 1 ]]; then
|
||||
jq . <<< "$secrets"
|
||||
else
|
||||
jq -r '.SecretList[].Name' <<< "$secrets"
|
||||
fi
|
||||
@@ -0,0 +1,48 @@
|
||||
name: secretsmanager
|
||||
help: Secrets Manager commands
|
||||
group: Secrets Manager
|
||||
expose: always
|
||||
dependencies:
|
||||
aws: Install the latest version following the instructions at 'https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html'
|
||||
jq: Install using 'brew install jq'
|
||||
|
||||
commands:
|
||||
- name: list-secrets
|
||||
help: List all AWS Secrets Manager secrets
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --detailed
|
||||
help: Output the list of all secrets in the detailed format
|
||||
|
||||
- name: show-secret
|
||||
help: Show the secret value for the specified secret
|
||||
args:
|
||||
- name: secret_id
|
||||
required: true
|
||||
help: The secret ID for which the value needs to be displayed
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --detailed
|
||||
help: Output the secret value in detailed format
|
||||
|
||||
- name: create-secret
|
||||
help: Create a new secret in Secrets Manager
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --name
|
||||
help: Name for the new secret
|
||||
required: true
|
||||
arg: name
|
||||
- long: --secret-string
|
||||
help: The secret string to be stored
|
||||
required: true
|
||||
arg: secret_string
|
||||
@@ -0,0 +1,16 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare detailed_format="${args[--detailed]}"
|
||||
declare secret_id="${args[secret_id]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
declare secret_value=$(aws secretsmanager get-secret-value --secret-id "$secret_id" --profile "$aws_profile" --region "$aws_region")
|
||||
|
||||
if [[ $detailed_format == 1 ]]; then
|
||||
jq . <<< "$secret_value"
|
||||
else
|
||||
jq '.SecretString' <<< "$secret_value" | sed 's|\\"|"|g' | sed -e 's/"{/{/' -e 's/}"/}/' | jq
|
||||
fi
|
||||
@@ -0,0 +1 @@
|
||||
aws --cli-auto-prompt
|
||||
@@ -0,0 +1,10 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare name="${args[--name]}"
|
||||
declare value="${args[--value]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
aws ssm put-parameter --name "$name" --value "$value" --type String --profile "$aws_profile" --region "$aws_region"
|
||||
@@ -0,0 +1,9 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare parameter_name="${args[parameter_name]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
aws ssm delete-parameter --name "$parameter_name" --profile "$aws_profile" --region "$aws_region"
|
||||
@@ -0,0 +1,16 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare detailed_format="${args[--detailed]}"
|
||||
declare parameter_name="${args[parameter_name]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
declare parameter_value=$(aws ssm get-parameter --name "$parameter_name" --profile "$aws_profile" --region "$aws_region")
|
||||
|
||||
if [[ $detailed_format == 1 ]]; then
|
||||
jq . <<< "$parameter_value"
|
||||
else
|
||||
jq '.Parameter.Value' <<< "$parameter_value" | tr -d '"'
|
||||
fi
|
||||
@@ -0,0 +1,16 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare detailed_format="${args[--detailed]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
declare parameters=$(aws ssm describe-parameters --profile "$aws_profile" --region "$aws_region")
|
||||
|
||||
if [[ $detailed_format == 1 ]]; then
|
||||
jq . <<< "$parameters"
|
||||
else
|
||||
jq -r '.Parameters[].Name' <<< "$parameters"
|
||||
fi
|
||||
@@ -0,0 +1,137 @@
|
||||
name: ssm
|
||||
help: SSM commands
|
||||
group: SSM
|
||||
expose: always
|
||||
dependencies:
|
||||
aws: Install the latest version following the instructions at 'https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html'
|
||||
jq: Install using 'brew install jq'
|
||||
|
||||
commands:
|
||||
- name: start-port-forwarding
|
||||
help: Use SSM to connect to an EC2 instance and forward a local port to the remote machine
|
||||
args:
|
||||
- name: instance-id
|
||||
help: The ID of the EC2 instance to connect to
|
||||
required: true
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --remote-port
|
||||
help: The port number of the server on the EC2 instance
|
||||
arg: remote-port
|
||||
default: "80"
|
||||
validate: aws_ssm_port_forwarding_number
|
||||
- long: --local-port
|
||||
help: The port number on the local machine to forward traffic to. An open port is chosen at run-time if not provided.
|
||||
arg: local-port
|
||||
default: "0"
|
||||
validate: aws_ssm_port_forwarding_number
|
||||
- long: --host
|
||||
help: Hostname or IP address of the destination server
|
||||
arg: host
|
||||
default: localhost
|
||||
validate: aws_ssm_port_forwarding_host
|
||||
examples:
|
||||
- dtools aws start-port-forwarding i-0892eeaed80a5b00b --remote-port 5432 --local-port 5432 --host prod-postgres.ctm8i4qgknv3.us-east-1.rds.amazonaws.com --profile prod --region us-east-1
|
||||
|
||||
- name: start-ngrok-bastion-instance
|
||||
help: Start an EC2 instance to act as a bastion host for ngrok
|
||||
dependencies:
|
||||
jq: Install with 'brew install jq'
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --hostname
|
||||
help: |
|
||||
The hostname to forward connections to.
|
||||
This will be hostnames like the ones that are only accessible via AWS ECS Service Discovery (e.g. api.caerus.local)
|
||||
required: true
|
||||
- long: --subnet-id
|
||||
help: The subnet ID that the instance is to be deployed into
|
||||
required: true
|
||||
- long: --port
|
||||
help: The port on the destination hostname to forward connections to
|
||||
arg: port
|
||||
default: "8080"
|
||||
- long: --ngrok-url
|
||||
short: -u
|
||||
arg: ngrok_url
|
||||
help: The ngrok URL to connect to
|
||||
required: true
|
||||
- long: --ngrok-auth-token
|
||||
short: -a
|
||||
arg: ngrok_auth_token
|
||||
help: The ngrok authentication token
|
||||
required: true
|
||||
|
||||
- name: list-parameters
|
||||
help: List all AWS SSM Parameter Store parameters
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --detailed
|
||||
help: Output the list of all parameters in the detailed format
|
||||
|
||||
- name: get-parameter
|
||||
help: Get the value of an AWS SSM Parameter Store parameter
|
||||
args:
|
||||
- name: parameter_name
|
||||
required: true
|
||||
help: The name of the parameter to retrieve
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --detailed
|
||||
help: Output the parameter value in detailed format
|
||||
|
||||
- name: create-parameter
|
||||
help: Create a new parameter in AWS SSM Parameter Store
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --name
|
||||
help: Name for the new parameter
|
||||
required: true
|
||||
arg: name
|
||||
- long: --value
|
||||
help: The value of the parameter to be stored
|
||||
required: true
|
||||
arg: value
|
||||
|
||||
- name: update-parameter
|
||||
help: Update an existing parameter in AWS SSM Parameter Store (Will create a new parameter if it does not exist)
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
- long: --name
|
||||
help: Name of the parameter to update
|
||||
required: true
|
||||
arg: name
|
||||
- long: --value
|
||||
help: The value of the parameter to be stored
|
||||
required: true
|
||||
arg: value
|
||||
|
||||
- name: delete-parameter
|
||||
help: Delete a parameter from AWS SSM Parameter Store
|
||||
filters:
|
||||
- profile_and_region_variables_set_with_flags
|
||||
flags:
|
||||
- import: src/components/aws/profile_flag.yml
|
||||
- import: src/components/aws/region_flag.yml
|
||||
args:
|
||||
- name: parameter_name
|
||||
required: true
|
||||
help: The name of the parameter to delete
|
||||
@@ -0,0 +1,108 @@
|
||||
set -e
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
# shellcheck disable=SC2154
|
||||
declare subnet_id="${args[--subnet-id]}"
|
||||
declare hostname="${args[--hostname]}"
|
||||
declare port="${args[--port]}"
|
||||
declare ngrok_url="${args[--ngrok-url]}"
|
||||
declare ngrok_auth_token="${args[--ngrok-auth-token]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "$instance_id" ]]; then
|
||||
yellow "Terminating the EC2 instance..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" ec2 terminate-instances --instance-ids "$instance_id"
|
||||
fi
|
||||
}
|
||||
|
||||
trap "cleanup" EXIT
|
||||
|
||||
cyan "Ensuring the AmazonSSMRoleForInstancesQuickSetup role exists..."
|
||||
if ! aws --profile "$aws_profile" --region "$aws_region" iam get-role --role-name AmazonSSMRoleForInstancesQuickSetup > /dev/null 2>&1; then
|
||||
yellow "Creating the AmazonSSMRoleForInstancesQuickSetup role..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" iam create-role \
|
||||
--role-name AmazonSSMRoleForInstancesQuickSetup \
|
||||
--assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Principal": {"Service": "ec2.amazonaws.com"}, "Action": "sts:AssumeRole"}]}' \
|
||||
> /dev/null
|
||||
|
||||
yellow "Attaching the AmazonSSMManagedInstanceCore policy to the role..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" iam attach-role-policy \
|
||||
--role-name AmazonSSMRoleForInstancesQuickSetup \
|
||||
--policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
|
||||
|
||||
yellow "Attaching the AmazonSSMPatchAssociation policy to the role..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" iam attach-role-policy \
|
||||
--role-name AmazonSSMRoleForInstancesQuickSetup \
|
||||
--policy-arn arn:aws:iam::aws:policy/AmazonSSMPatchAssociation
|
||||
|
||||
yellow "Creating the AmazonSSMRoleForInstancesQuickSetup instance profile..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" iam create-instance-profile \
|
||||
--instance-profile-name AmazonSSMRoleForInstancesQuickSetup \
|
||||
> /dev/null
|
||||
|
||||
yellow "Adding the AmazonSSMRoleForInstancesQuickSetup role to the instance profile..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" iam add-role-to-instance-profile \
|
||||
--instance-profile-name AmazonSSMRoleForInstancesQuickSetup --role-name AmazonSSMRoleForInstancesQuickSetup \
|
||||
> /dev/null
|
||||
sleep 5
|
||||
fi
|
||||
|
||||
cyan "Launching an EC2 instance..."
|
||||
# shellcheck disable=SC2155
|
||||
declare instance_id=$({
|
||||
aws --profile "$aws_profile" --region "$aws_region" ec2 run-instances \
|
||||
--image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 \
|
||||
--instance-type t2.micro \
|
||||
--count 1 \
|
||||
--subnet-id "$subnet_id" \
|
||||
--iam-instance-profile Name=AmazonSSMRoleForInstancesQuickSetup \
|
||||
--user-data $'#!/bin/bash\nwget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz\ntar xvzf ./ngrok-v3-stable-linux-amd64.tgz -C /usr/local/bin' \
|
||||
--query Instances[0].InstanceId \
|
||||
--output text
|
||||
})
|
||||
|
||||
get-instance-state() {
|
||||
aws --profile "$aws_profile" --region "$aws_region" ec2 describe-instance-status \
|
||||
--instance-ids "$instance_id" \
|
||||
--query InstanceStatuses[0] |\
|
||||
jq '. | {instance: .InstanceStatus.Details[0].Status, system: .SystemStatus.Details[0].Status}'
|
||||
}
|
||||
|
||||
status_checks=$(get-instance-state)
|
||||
until [[ $(jq -r '.instance' <<< "$status_checks") == "passed" && $(jq -r '.system' <<< "$status_checks") == "passed" ]]; do
|
||||
yellow "Waiting for instance to start..."
|
||||
sleep 1
|
||||
status_checks=$(get-instance-state)
|
||||
done
|
||||
|
||||
green 'Instance is running!'
|
||||
|
||||
yellow "Adding the ngrok authtoken to the instance..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" ssm start-session \
|
||||
--target "$instance_id" \
|
||||
--document-name AWS-StartInteractiveCommand \
|
||||
--parameters command="ngrok config add-authtoken $ngrok_auth_token"
|
||||
|
||||
yellow 'Starting ngrok tunnel...'
|
||||
cyan 'The resource will be available at the following URL: '
|
||||
cyan_bold "https://$ngrok_url"
|
||||
|
||||
cyan "\nYou will be able to point Postman to the above URL to access the resource."
|
||||
|
||||
yellow_bold "\nPress 'Ctrl+C' to stop the ngrok tunnel and to terminate the EC2 instance."
|
||||
|
||||
red_bold "This information will only be displayed once. Please make a note of it.\n"
|
||||
|
||||
read -rp "To acknowledge receipt and continue, press 'Enter'." </dev/tty
|
||||
|
||||
aws --profile "$aws_profile" --region "$aws_region" ssm start-session \
|
||||
--target "$instance_id" \
|
||||
--document-name AWS-StartInteractiveCommand \
|
||||
--parameters command="ngrok http ${hostname}:${port} --domain $ngrok_url"
|
||||
|
||||
yellow "Terminating the EC2 instance..."
|
||||
aws --profile "$aws_profile" --region "$aws_region" ec2 terminate-instances --instance-ids "$instance_id"
|
||||
@@ -0,0 +1,17 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
declare aws_region="$(get-aws-region)"
|
||||
# shellcheck disable=SC2154
|
||||
declare instance_id="${args[instance-id]}"
|
||||
declare remote_port="${args[--remote-port]}"
|
||||
declare local_port="${args[--local-port]}"
|
||||
declare host="${args[--host]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
aws ssm start-session \
|
||||
--profile "$aws_profile" \
|
||||
--region "$aws_region" \
|
||||
--target "$instance_id" \
|
||||
--document-name "AWS-StartPortForwardingSessionToRemoteHost" \
|
||||
--parameters "portNumber=${remote_port},localPortNumber=${local_port},host=${host}"
|
||||
@@ -0,0 +1,10 @@
|
||||
# shellcheck disable=SC2155
|
||||
declare aws_region="$(get-aws-region)"
|
||||
declare aws_profile="$(get-aws-profile)"
|
||||
# shellcheck disable=SC2154
|
||||
declare name="${args[--name]}"
|
||||
declare value="${args[--value]}"
|
||||
|
||||
validate-or-refresh-aws-auth
|
||||
|
||||
aws ssm put-parameter --name "$name" --value "$value" --overwrite --profile "$aws_profile" --region "$aws_region"
|
||||
@@ -0,0 +1,13 @@
|
||||
set-aws-auto-prompt() {
|
||||
if ( grep "AWS_CLI_AUTO_PROMPT" ~/.bashrc > /dev/null 2>&1 ); then
|
||||
sed -i "/AWS_CLI_AUTO_PROMPT=/c\export AWS_CLI_AUTO_PROMPT=$1" ~/.bashrc
|
||||
fi
|
||||
|
||||
bash -c "export AWS_CLI_AUTO_PROMPT=$1; exec bash"
|
||||
}
|
||||
|
||||
if [[ -z ${AWS_CLI_AUTO_PROMPT} || $AWS_CLI_AUTO_PROMPT == 'off' ]]; then
|
||||
set-aws-auto-prompt on
|
||||
else
|
||||
set-aws-auto-prompt off
|
||||
fi
|
||||
Reference in New Issue
Block a user