nate2-scripts 2.0.0__tar.gz → 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nate2_scripts-2.0.1/PKG-INFO +221 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/README.md +2 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/pyproject.toml +2 -1
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/__init__.py +1 -1
- nate2_scripts-2.0.1/src/nate2_scripts/console/resolvers/datasync.py +11 -0
- nate2_scripts-2.0.1/src/nate2_scripts/console/resolvers/scheduler.py +11 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/text_resolver.py +4 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/__init__.py +14 -0
- nate2_scripts-2.0.1/tests/console/resolvers/datasync.py +34 -0
- nate2_scripts-2.0.1/tests/console/resolvers/scheduler.py +34 -0
- nate2_scripts-2.0.0/CONSOLE-NOTES.md +0 -70
- nate2_scripts-2.0.0/PKG-INFO +0 -13
- nate2_scripts-2.0.0/i-want-to-refactor-fizzy-feather.md +0 -173
- nate2_scripts-2.0.0/plan.md +0 -16
- nate2_scripts-2.0.0/test.py +0 -100
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/.github/workflows/publish.yml +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/.gitignore +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/LICENSE +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/__init__.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/bing_wrapper/__init__.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/bing_wrapper/autocomplete.sh +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/boto3_session.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/hash_grabber.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/local_refresh.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/__init__.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/apigateway.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/artifact.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/athena.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/batch.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/bedrock.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/cloudformation.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/cloudfront.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/cloudhsm.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/cloudtrail.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/cognito.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/config.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/controltower.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/cost.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/dynamo.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/ec2.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/ecr.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/ecs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/efs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/eks.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/elasticbeanstalk.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/elb.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/eventbridge.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/fsx.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/glue.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/guardduty.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/iam.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/inspector.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/kms.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/lambda_.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/lex.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/lightsail.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/logs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/marketplace.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/polly.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/quick.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/rds.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/route53.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/s3.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/sagemaker.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/secretsmanager.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/sns.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/sqs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/ssm.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/stepfunctions.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/storagegateway.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/support.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/resolvers/vpc.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/session_cache.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/src/nate2_scripts/console/types.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/__init__.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/__init__.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/apigateway.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/artifact.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/athena.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/batch.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/bedrock.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/cloudformation.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/cloudfront.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/cloudhsm.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/cloudtrail.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/cognito.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/config.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/conftest.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/controltower.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/cost.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/dynamo.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/ec2.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/ecr.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/ecs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/efs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/eks.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/elasticbeanstalk.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/elb.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/eventbridge.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/fsx.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/glue.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/guardduty.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/iam.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/inspector.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/kms.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/lambda_.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/lex.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/lightsail.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/logs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/marketplace.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/polly.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/rds.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/route53.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/s3.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/sagemaker.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/secretsmanager.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/sns.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/sqs.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/ssm.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/stepfunctions.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/storagegateway.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/support.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/resolvers/vpc.py +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/test_aws/config +0 -0
- {nate2_scripts-2.0.0 → nate2_scripts-2.0.1}/tests/console/test_aws/credentials +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: nate2-scripts
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: Utility scripts for AWS development on macOS
|
|
5
|
+
Project-URL: Homepage, https://github.com/tennex-nwestfall/nate2-scripts
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: argcomplete
|
|
9
|
+
Requires-Dist: boto3
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: boto3-stubs[essential]; extra == 'dev'
|
|
12
|
+
Requires-Dist: moto[all]; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
[](https://pypi.org/project/nate2-scripts/)
|
|
17
|
+
[](https://github.com/tennex-nwestfall/nate2-scripts/blob/main/LICENSE)
|
|
18
|
+
|
|
19
|
+
# Nate2's Scripts
|
|
20
|
+
|
|
21
|
+
Utility scripts for AWS development on macOS.
|
|
22
|
+
|
|
23
|
+
To install run:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install nate2-scripts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To update run:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install --upgrade nate2-scripts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Scripts
|
|
36
|
+
|
|
37
|
+
### `console`
|
|
38
|
+
|
|
39
|
+
Opens the AWS Management Console in your browser for a specific service, ARN, resource id, or resource name, using federated sign-in from your current AWS credentials. Supports multi-session via searching browser history sqlite databases.
|
|
40
|
+
|
|
41
|
+
**Requirements:**
|
|
42
|
+
|
|
43
|
+
- `AWS_PROFILE` set (e.g. via `asp`)
|
|
44
|
+
- `git config user.email` set — it is used as the `role_session_name` for every assume-role profile
|
|
45
|
+
|
|
46
|
+
**Example Alias:**
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# noglob is so that one can paste in logs arns that end with :*
|
|
50
|
+
# otherwise zsh tries to see the log arn as a glob and fails
|
|
51
|
+
alias co="noglob console"
|
|
52
|
+
# needed for autocomplete
|
|
53
|
+
eval "$(register-python-argcomplete console)"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Usage:**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
console [service | arn | id | name] [search ...] [-r REGION] [-f]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# change to an account/role
|
|
64
|
+
asp my-aws-role
|
|
65
|
+
|
|
66
|
+
# open main page of the role/account
|
|
67
|
+
console
|
|
68
|
+
# open ec2 page of the role/account
|
|
69
|
+
console ec2
|
|
70
|
+
# open lambda page of the role/account in us-west-2
|
|
71
|
+
console lambda -r us-west-2
|
|
72
|
+
# open a service page filtered by search terms (multiple words are joined)
|
|
73
|
+
console lambda my-function
|
|
74
|
+
console logs /aws/lambda/my
|
|
75
|
+
console cf my stack
|
|
76
|
+
# open instance id page of the role/account in
|
|
77
|
+
# whatever region that instance is in
|
|
78
|
+
console i-0123456789abcdef
|
|
79
|
+
# open an S3 bucket, prefix, or object
|
|
80
|
+
console s3://my-bucket/some/prefix/
|
|
81
|
+
# open a resource by name (log group, S3 bucket, EC2 Name tag,
|
|
82
|
+
# CloudFormation stack, or Lambda function)
|
|
83
|
+
console my-function
|
|
84
|
+
# open the console page for the resource the ARN points to
|
|
85
|
+
console arn:aws:ec2:us-east-1:012345678967:instance/i-0123456789abcdef
|
|
86
|
+
# force a new federated sign-in (resets the session cache)
|
|
87
|
+
console -f
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Options:**
|
|
91
|
+
|
|
92
|
+
| Flag | Description |
|
|
93
|
+
| ----------------------- | ----------------------------------------------------------------- |
|
|
94
|
+
| `-r`, `--region <name>` | Region to open the console in; also added to the searched regions |
|
|
95
|
+
| `-f`, `--force` | Reset the session cache and force a new federated sign-in |
|
|
96
|
+
|
|
97
|
+
**Resolution order:**
|
|
98
|
+
|
|
99
|
+
1. **ARN** — if the first argument parses as an ARN
|
|
100
|
+
2. **Service** — if the first argument is a known service name or alias
|
|
101
|
+
3. **Resource id** — if the first argument matches a known id pattern
|
|
102
|
+
4. **Resource name** — looked up in parallel across all name-resolving services; the first match in priority order wins: CloudWatch log group → S3 bucket → EC2 `Name` tag → CloudFormation stack → Lambda function
|
|
103
|
+
|
|
104
|
+
**Supported services** (aliases after the `/`):
|
|
105
|
+
|
|
106
|
+
| Service | Names | Search terms |
|
|
107
|
+
| ---------------------- | ----------------------------------------- | ------------ |
|
|
108
|
+
| API Gateway | `apigateway` / `api` | |
|
|
109
|
+
| Artifact | `artifact` | |
|
|
110
|
+
| Athena | `athena` | |
|
|
111
|
+
| Batch | `batch` / `job` / `jobs` | |
|
|
112
|
+
| Bedrock | `bedrock` / `br` | |
|
|
113
|
+
| CloudFormation | `cloudformation` / `cf` | ✓ |
|
|
114
|
+
| CloudFront | `cloudfront` / `front` | |
|
|
115
|
+
| CloudHSM | `cloudhsm` / `hsm` | |
|
|
116
|
+
| CloudTrail | `cloudtrail` / `ct` | |
|
|
117
|
+
| CloudWatch | `cloudwatch` / `cw` | |
|
|
118
|
+
| CloudWatch Logs | `logs` | ✓ |
|
|
119
|
+
| Cognito | `cognito` / `cn` | |
|
|
120
|
+
| Config | `config` / `cfg` | |
|
|
121
|
+
| Control Tower | `controltower` | |
|
|
122
|
+
| Cost Management | `cost` | |
|
|
123
|
+
| DataSync | `datasync` / `ds` | |
|
|
124
|
+
| DynamoDB | `dynamodb` / `dynamo` / `dyn` | |
|
|
125
|
+
| EC2 instances | `ec2` | ✓ |
|
|
126
|
+
| EC2 AMIs (owned by me) | `ami` | ✓ |
|
|
127
|
+
| ECR | `ecr` / `docker` | |
|
|
128
|
+
| ECS | `ecs` / `cluster` / `task` / `tasks` | |
|
|
129
|
+
| EFS | `efs` | |
|
|
130
|
+
| EKS | `eks` | |
|
|
131
|
+
| Elastic Beanstalk | `elasticbeanstalk` / `bean` | |
|
|
132
|
+
| Elastic Load Balancing | `elb` / `alb` | |
|
|
133
|
+
| EventBridge | `eventbridge` / `eb` | |
|
|
134
|
+
| FSx | `fsx` | |
|
|
135
|
+
| Glue | `glue` | |
|
|
136
|
+
| GuardDuty | `guardduty` / `guard` | |
|
|
137
|
+
| IAM | `iam` | |
|
|
138
|
+
| Inspector | `inspector` | |
|
|
139
|
+
| KMS | `kms` | |
|
|
140
|
+
| Lambda | `lambda` | ✓ |
|
|
141
|
+
| Lex | `lex` | |
|
|
142
|
+
| Lightsail | `lightsail` / `ls` | |
|
|
143
|
+
| Marketplace | `marketplace` / `market` / `tennex` | ✓ |
|
|
144
|
+
| Polly | `polly` | |
|
|
145
|
+
| RDS | `rds` | |
|
|
146
|
+
| Route 53 | `route53` / `53` | |
|
|
147
|
+
| S3 | `s3` | |
|
|
148
|
+
| SageMaker | `sagemaker` / `sage` | |
|
|
149
|
+
| Scheduler | `scheduler` / `sch` | |
|
|
150
|
+
| Secrets Manager | `secretsmanager` / `secretmanager` / `sm` | |
|
|
151
|
+
| Security groups | `sg` | ✓ |
|
|
152
|
+
| SNS | `sns` | |
|
|
153
|
+
| SQS | `sqs` | |
|
|
154
|
+
| Step Functions | `step` | |
|
|
155
|
+
| Storage Gateway | `storagegateway` / `sgw` | |
|
|
156
|
+
| Support | `support` | |
|
|
157
|
+
| Systems Manager | `ssm` | |
|
|
158
|
+
| SSM Parameter Store | `parameter` / `ps` | ✓ |
|
|
159
|
+
| VPC | `vpc` | |
|
|
160
|
+
| VPC subnets | `subnet` | |
|
|
161
|
+
| VPC network ACLs | `acl` / `nacl` | |
|
|
162
|
+
| VPC Elastic IPs | `elasticip` / `eni` | |
|
|
163
|
+
| VPC route tables | `routetable` / `routes` | |
|
|
164
|
+
| VPC internet gateways | `internetgateway` / `internet` / `igw` | |
|
|
165
|
+
| VPC NAT gateways | `nat` | |
|
|
166
|
+
| VPC endpoints | `endpoints` / `end` | |
|
|
167
|
+
| VPC transit gateways | `transitgateway` / `transit` / `tgw` | |
|
|
168
|
+
|
|
169
|
+
`tennex` opens a Marketplace search for "tennex". Cost Management and Marketplace always open in `us-east-1`.
|
|
170
|
+
|
|
171
|
+
**Supported resource ids:** `i-*` (EC2 instance), `sg-*` (security group), `ami-*`, `lt-*` (launch template), `vol-*` (EBS volume), `snap-*` (EBS snapshot), `eni-*` (network interface), `s3://bucket[/key]`
|
|
172
|
+
|
|
173
|
+
**Supported ARNs:** `ec2` (instance), `lambda` (function), `logs` (log group, log stream), `s3` (bucket, prefix, object)
|
|
174
|
+
|
|
175
|
+
**Cross-account ARNs:** if an ARN's account differs from the current one, `console` switches to the first profile in your AWS config whose `role_arn` is in that account.
|
|
176
|
+
|
|
177
|
+
**Regions:** ids and names are searched across `us-east-1`, `us-east-2`, `us-west-1`, and `us-west-2`, plus the region passed with `-r`.
|
|
178
|
+
|
|
179
|
+
**Session cache:** federated session hashes are cached per profile for 59 minutes in `$XDG_STATE_HOME/console-sessions.json` (default `~/.local/state/console-sessions.json`). Use `-f` to reset it.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### `bing-wrapper`
|
|
184
|
+
|
|
185
|
+
A PTY wrapper that runs any command in a pseudo-terminal and plays a macOS system sound when a configurable output pattern is detected, and/or when the command finishes. Useful for long-running CLI tools (e.g. CDK deployments) that prompt for confirmation or take a while to complete.
|
|
186
|
+
|
|
187
|
+
**Requirements:** None
|
|
188
|
+
|
|
189
|
+
**Example Alias Usage:**
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# wraps cdk to play Frog.aiff when done and play Glass.aiff (default) on "(y/n)" prompts
|
|
193
|
+
alias cdk="bing-wrapper --end --pattern \"(y/n)\" --sound-end /System/Library/Sounds/Frog.aiff -- cdk"
|
|
194
|
+
|
|
195
|
+
# wraps nextflow to play a sound when done
|
|
196
|
+
alias nextflow="bing-wrapper --end -- nextflow"
|
|
197
|
+
|
|
198
|
+
# needed for autocomplete of bing-wrapper and the inner command
|
|
199
|
+
eval "$(bing-wrapper --autocomplete)"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Options:**
|
|
203
|
+
|
|
204
|
+
| Flag | Description | Default |
|
|
205
|
+
| ------------------------ | --------------------------------------------------------- | ------------ |
|
|
206
|
+
| `--end` | Play a sound when the command exits | off |
|
|
207
|
+
| `--pattern <text>` | Play a sound when `<text>` appears in output (repeatable) | none |
|
|
208
|
+
| `--sound-end <path>` | Sound file to play on exit | `Glass.aiff` |
|
|
209
|
+
| `--sound-pattern <path>` | Sound file to play on pattern match | `Glass.aiff` |
|
|
210
|
+
| `--sound-while <path>` | Sound file to play on loop while the command runs | none |
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pip install -e ".[dev]"
|
|
218
|
+
pytest
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Resolver tests live in `tests/console/resolvers/`, one module per service, and use `moto` to mock AWS.
|
|
@@ -105,6 +105,7 @@ console -f
|
|
|
105
105
|
| Config | `config` / `cfg` | |
|
|
106
106
|
| Control Tower | `controltower` | |
|
|
107
107
|
| Cost Management | `cost` | |
|
|
108
|
+
| DataSync | `datasync` / `ds` | |
|
|
108
109
|
| DynamoDB | `dynamodb` / `dynamo` / `dyn` | |
|
|
109
110
|
| EC2 instances | `ec2` | ✓ |
|
|
110
111
|
| EC2 AMIs (owned by me) | `ami` | ✓ |
|
|
@@ -130,6 +131,7 @@ console -f
|
|
|
130
131
|
| Route 53 | `route53` / `53` | |
|
|
131
132
|
| S3 | `s3` | |
|
|
132
133
|
| SageMaker | `sagemaker` / `sage` | |
|
|
134
|
+
| Scheduler | `scheduler` / `sch` | |
|
|
133
135
|
| Secrets Manager | `secretsmanager` / `secretmanager` / `sm` | |
|
|
134
136
|
| Security groups | `sg` | ✓ |
|
|
135
137
|
| SNS | `sns` | |
|
|
@@ -4,8 +4,9 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nate2-scripts"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.1"
|
|
8
8
|
description = "Utility scripts for AWS development on macOS"
|
|
9
|
+
readme = "README.md"
|
|
9
10
|
requires-python = ">=3.11"
|
|
10
11
|
dependencies = [
|
|
11
12
|
"argcomplete",
|
|
@@ -194,7 +194,7 @@ def main() -> None:
|
|
|
194
194
|
print("Default region is: ", session.region_name)
|
|
195
195
|
|
|
196
196
|
suffix, context = get_suffix(args.service, args.search, args.region, session)
|
|
197
|
-
if suffix:
|
|
197
|
+
if suffix is not None:
|
|
198
198
|
open_console(suffix, context, session_cache)
|
|
199
199
|
else:
|
|
200
200
|
print("Cannot open console page.")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from nate2_scripts.console.resolvers import Context, Resolver
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DataSyncResolver(Resolver):
|
|
5
|
+
def get_service_names(self) -> list[str]:
|
|
6
|
+
return ["datasync", "ds"]
|
|
7
|
+
|
|
8
|
+
def try_resolve_service(
|
|
9
|
+
self, context: Context, service: str, search: str
|
|
10
|
+
) -> str | None:
|
|
11
|
+
return "/datasync/home#/tasks"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from nate2_scripts.console.resolvers import Context, Resolver
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SchedulerResolver(Resolver):
|
|
5
|
+
def get_service_names(self) -> list[str]:
|
|
6
|
+
return ["scheduler", "sch"]
|
|
7
|
+
|
|
8
|
+
def try_resolve_service(
|
|
9
|
+
self, context: Context, service: str, search: str
|
|
10
|
+
) -> str | None:
|
|
11
|
+
return "/scheduler/home#schedules"
|
|
@@ -16,6 +16,7 @@ from nate2_scripts.console.resolvers.cognito import CognitoResolver
|
|
|
16
16
|
from nate2_scripts.console.resolvers.config import ConfigResolver
|
|
17
17
|
from nate2_scripts.console.resolvers.controltower import ControlTowerResolver
|
|
18
18
|
from nate2_scripts.console.resolvers.cost import CostResolver
|
|
19
|
+
from nate2_scripts.console.resolvers.datasync import DataSyncResolver
|
|
19
20
|
from nate2_scripts.console.resolvers.dynamo import DynamoResolver
|
|
20
21
|
from nate2_scripts.console.resolvers.ec2 import Ec2Resolver
|
|
21
22
|
from nate2_scripts.console.resolvers.ecr import EcrResolver
|
|
@@ -41,6 +42,7 @@ from nate2_scripts.console.resolvers.rds import RdsResolver
|
|
|
41
42
|
from nate2_scripts.console.resolvers.route53 import Route53Resolver
|
|
42
43
|
from nate2_scripts.console.resolvers.s3 import S3Resolver
|
|
43
44
|
from nate2_scripts.console.resolvers.sagemaker import SagemakerResolver
|
|
45
|
+
from nate2_scripts.console.resolvers.scheduler import SchedulerResolver
|
|
44
46
|
from nate2_scripts.console.resolvers.secretsmanager import SecretsManagerResolver
|
|
45
47
|
from nate2_scripts.console.resolvers.sns import SnsResolver
|
|
46
48
|
from nate2_scripts.console.resolvers.sqs import SqsResolver
|
|
@@ -73,6 +75,7 @@ RESOLVERS: list[Resolver] = [
|
|
|
73
75
|
ConfigResolver(),
|
|
74
76
|
ControlTowerResolver(),
|
|
75
77
|
CostResolver(),
|
|
78
|
+
DataSyncResolver(),
|
|
76
79
|
DynamoResolver(),
|
|
77
80
|
EcrResolver(),
|
|
78
81
|
EcsResolver(),
|
|
@@ -94,6 +97,7 @@ RESOLVERS: list[Resolver] = [
|
|
|
94
97
|
RdsResolver(),
|
|
95
98
|
Route53Resolver(),
|
|
96
99
|
SagemakerResolver(),
|
|
100
|
+
SchedulerResolver(),
|
|
97
101
|
SecretsManagerResolver(),
|
|
98
102
|
SnsResolver(),
|
|
99
103
|
SqsResolver(),
|
|
@@ -3,6 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
import boto3
|
|
4
4
|
import pytest
|
|
5
5
|
|
|
6
|
+
from nate2_scripts.console import get_suffix
|
|
6
7
|
from nate2_scripts.console.resolvers import Arn
|
|
7
8
|
from nate2_scripts.console.text_resolver import RESOLVERS
|
|
8
9
|
|
|
@@ -27,3 +28,16 @@ def test_resolver_has_test_file(resolver):
|
|
|
27
28
|
assert (TEST_DIR / f"{module_name}.py").is_file(), (
|
|
28
29
|
f"{type(resolver).__name__} has no tests/console/resolvers/{module_name}.py"
|
|
29
30
|
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_base():
|
|
34
|
+
session = make_session("fake-A-org")
|
|
35
|
+
suffix, context = get_suffix(
|
|
36
|
+
"",
|
|
37
|
+
"this parameter isn't possible",
|
|
38
|
+
"",
|
|
39
|
+
session,
|
|
40
|
+
)
|
|
41
|
+
assert suffix == ""
|
|
42
|
+
assert context.session.region_name == "us-east-1"
|
|
43
|
+
assert context.identity["Account"] == "111111111111"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from nate2_scripts.console import get_suffix
|
|
2
|
+
from tests.console.resolvers import make_session
|
|
3
|
+
|
|
4
|
+
DATASYNC_CONSOLE_URL = "/datasync/home#/tasks"
|
|
5
|
+
|
|
6
|
+
# --------------------------------
|
|
7
|
+
# Service
|
|
8
|
+
# --------------------------------
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_service_datasync():
|
|
12
|
+
session = make_session("fake-A-org")
|
|
13
|
+
suffix, context = get_suffix(
|
|
14
|
+
"datasync",
|
|
15
|
+
"this parameter doesn't matter",
|
|
16
|
+
"",
|
|
17
|
+
session,
|
|
18
|
+
)
|
|
19
|
+
assert suffix == f"{DATASYNC_CONSOLE_URL}"
|
|
20
|
+
assert context.session.region_name == "us-east-1"
|
|
21
|
+
assert context.identity["Account"] == "111111111111"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_service_ds():
|
|
25
|
+
session = make_session("fake-A-org")
|
|
26
|
+
suffix, context = get_suffix(
|
|
27
|
+
"ds",
|
|
28
|
+
"this parameter doesn't matter",
|
|
29
|
+
"us-west-2",
|
|
30
|
+
session,
|
|
31
|
+
)
|
|
32
|
+
assert suffix == f"{DATASYNC_CONSOLE_URL}"
|
|
33
|
+
assert context.session.region_name == "us-west-2"
|
|
34
|
+
assert context.identity["Account"] == "111111111111"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from nate2_scripts.console import get_suffix
|
|
2
|
+
from tests.console.resolvers import make_session
|
|
3
|
+
|
|
4
|
+
SCHEDULER_CONSOLE_URL = "/scheduler/home#schedules"
|
|
5
|
+
|
|
6
|
+
# --------------------------------
|
|
7
|
+
# Service
|
|
8
|
+
# --------------------------------
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_service_scheduler():
|
|
12
|
+
session = make_session("fake-A-org")
|
|
13
|
+
suffix, context = get_suffix(
|
|
14
|
+
"scheduler",
|
|
15
|
+
"this parameter doesn't matter",
|
|
16
|
+
"",
|
|
17
|
+
session,
|
|
18
|
+
)
|
|
19
|
+
assert suffix == f"{SCHEDULER_CONSOLE_URL}"
|
|
20
|
+
assert context.session.region_name == "us-east-1"
|
|
21
|
+
assert context.identity["Account"] == "111111111111"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_service_sch():
|
|
25
|
+
session = make_session("fake-A-org")
|
|
26
|
+
suffix, context = get_suffix(
|
|
27
|
+
"sch",
|
|
28
|
+
"this parameter doesn't matter",
|
|
29
|
+
"us-west-2",
|
|
30
|
+
session,
|
|
31
|
+
)
|
|
32
|
+
assert suffix == f"{SCHEDULER_CONSOLE_URL}"
|
|
33
|
+
assert context.session.region_name == "us-west-2"
|
|
34
|
+
assert context.identity["Account"] == "111111111111"
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Console Script — Session Investigation Notes
|
|
2
|
-
|
|
3
|
-
## The Problem
|
|
4
|
-
|
|
5
|
-
Running `co lambda` then `co ec2` (same account) signs out the Lambda tab.
|
|
6
|
-
|
|
7
|
-
## Root Cause
|
|
8
|
-
|
|
9
|
-
The session cache keys by `AccessKeyId`, which rotates every time Okta refreshes STS credentials (~1 hour). After rotation, the cache misses and re-federates, creating a new browser session that invalidates the previous one.
|
|
10
|
-
|
|
11
|
-
## How Federation Works
|
|
12
|
-
|
|
13
|
-
1. CLI credentials (AccessKeyId/SecretAccessKey/SessionToken) are sent to `signin.aws.amazon.com/federation?Action=getSigninToken` → returns a one-time SigninToken
|
|
14
|
-
2. Browser opens `signin.aws.amazon.com/federation?Action=login&SigninToken=...` → AWS creates a browser console session (cookies) and redirects to destination
|
|
15
|
-
3. The browser session lasts **12 hours** (script uses 11hr buffer)
|
|
16
|
-
4. Each `Action=login` call for the **same account** replaces the previous session → other tabs for that account become invalid
|
|
17
|
-
5. Different accounts get isolated sessions (multi-session console, 2024+) — no conflict
|
|
18
|
-
|
|
19
|
-
## Key Findings
|
|
20
|
-
|
|
21
|
-
- **Federation does NOT require existing browser cookies** — it creates a session from scratch using CLI creds
|
|
22
|
-
- **Multi-session console** isolates different accounts but NOT multiple sessions within the same account
|
|
23
|
-
- **Re-federating same account** kills existing tabs for that account
|
|
24
|
-
- **The account-specific URL hash** (e.g., `lh2nfmmd` in `905502466771-lh2nfmmd.us-east-1.console.aws.amazon.com`) is per-browser-session, changes on every sign-in, and is NOT retrievable via any AWS API. It's assigned client-side by JavaScript after login.
|
|
25
|
-
- **The hash is NOT cacheable** — it's tied to the browser session lifetime, not the account
|
|
26
|
-
- **DNS lookups** won't help — `*.console.aws.amazon.com` is a wildcard record; the hash is resolved by cookies/frontend
|
|
27
|
-
|
|
28
|
-
## The Fix
|
|
29
|
-
|
|
30
|
-
Key the session cache by **profile name** (or account ID) instead of `AccessKeyId`:
|
|
31
|
-
|
|
32
|
-
- The browser session is tied to the account, not the specific credential set
|
|
33
|
-
- Even after Okta rotates creds (new AccessKeyId), the browser session from the previous federation is still alive
|
|
34
|
-
- Cache should prevent re-federation as long as it's within 11 hours of the last federation for that profile
|
|
35
|
-
|
|
36
|
-
## Approaches Considered & Rejected
|
|
37
|
-
|
|
38
|
-
| Approach | Why rejected |
|
|
39
|
-
|----------|-------------|
|
|
40
|
-
| Always federate | Signs out other tabs for same account |
|
|
41
|
-
| Account-specific URL hash (cached) | Hash is per-session, not stable |
|
|
42
|
-
| Selenium/Playwright to discover hash | Hash from WebDriver session ≠ real browser session |
|
|
43
|
-
| AppleScript to read browser URL | Hash is session-bound, same lifecycle as session cache |
|
|
44
|
-
| Browser profiles (Chrome/Firefox) | Still can't open 2nd tab without re-federation within same profile |
|
|
45
|
-
| DNS lookup for hash | Wildcard DNS — hash is cookie-routed, not DNS-routed |
|
|
46
|
-
| Capture hash from federation redirect | Redirect goes to the generic URL; hash rewrite is client-side JS |
|
|
47
|
-
|
|
48
|
-
## Current Architecture
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
co <service>
|
|
52
|
-
→ check AWS_PROFILE is set
|
|
53
|
-
→ parse destination URL
|
|
54
|
-
→ get CLI creds (aws configure export-credentials)
|
|
55
|
-
→ check session cache
|
|
56
|
-
→ valid: open raw URL (webbrowser.open)
|
|
57
|
-
→ expired/missing: federate → open federation URL → record cache
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## What Needs to Change
|
|
61
|
-
|
|
62
|
-
```python
|
|
63
|
-
# Before (buggy): keyed by AccessKeyId which rotates
|
|
64
|
-
cache[creds["AccessKeyId"]] = {"federated_at": time.time()}
|
|
65
|
-
|
|
66
|
-
# After: keyed by profile name which is stable
|
|
67
|
-
cache[profile_name] = {"federated_at": time.time()}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
This ensures that within the 11-hour window, `co` opens the raw URL without re-federating, keeping other same-account tabs alive.
|
nate2_scripts-2.0.0/PKG-INFO
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: nate2-scripts
|
|
3
|
-
Version: 2.0.0
|
|
4
|
-
Summary: Utility scripts for AWS development on macOS
|
|
5
|
-
Project-URL: Homepage, https://github.com/tennex-nwestfall/nate2-scripts
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.11
|
|
8
|
-
Requires-Dist: argcomplete
|
|
9
|
-
Requires-Dist: boto3
|
|
10
|
-
Provides-Extra: dev
|
|
11
|
-
Requires-Dist: boto3-stubs[essential]; extra == 'dev'
|
|
12
|
-
Requires-Dist: moto[all]; extra == 'dev'
|
|
13
|
-
Requires-Dist: pytest; extra == 'dev'
|