nate2-scripts 2.0.0__tar.gz → 2.0.2__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.
Files changed (127) hide show
  1. nate2_scripts-2.0.2/PKG-INFO +221 -0
  2. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/README.md +2 -0
  3. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/pyproject.toml +2 -1
  4. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/__init__.py +10 -2
  5. nate2_scripts-2.0.2/src/nate2_scripts/console/resolvers/backup.py +11 -0
  6. nate2_scripts-2.0.2/src/nate2_scripts/console/resolvers/datasync.py +11 -0
  7. nate2_scripts-2.0.2/src/nate2_scripts/console/resolvers/scheduler.py +11 -0
  8. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/text_resolver.py +6 -0
  9. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/__init__.py +14 -0
  10. nate2_scripts-2.0.2/tests/console/resolvers/backup.py +36 -0
  11. nate2_scripts-2.0.2/tests/console/resolvers/datasync.py +34 -0
  12. nate2_scripts-2.0.2/tests/console/resolvers/scheduler.py +34 -0
  13. nate2_scripts-2.0.0/CONSOLE-NOTES.md +0 -70
  14. nate2_scripts-2.0.0/PKG-INFO +0 -13
  15. nate2_scripts-2.0.0/i-want-to-refactor-fizzy-feather.md +0 -173
  16. nate2_scripts-2.0.0/plan.md +0 -16
  17. nate2_scripts-2.0.0/test.py +0 -100
  18. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/.github/workflows/publish.yml +0 -0
  19. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/.gitignore +0 -0
  20. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/LICENSE +0 -0
  21. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/__init__.py +0 -0
  22. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/bing_wrapper/__init__.py +0 -0
  23. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/bing_wrapper/autocomplete.sh +0 -0
  24. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/boto3_session.py +0 -0
  25. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/hash_grabber.py +0 -0
  26. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/local_refresh.py +0 -0
  27. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/__init__.py +0 -0
  28. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/apigateway.py +0 -0
  29. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/artifact.py +0 -0
  30. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/athena.py +0 -0
  31. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/batch.py +0 -0
  32. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/bedrock.py +0 -0
  33. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/cloudformation.py +0 -0
  34. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/cloudfront.py +0 -0
  35. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/cloudhsm.py +0 -0
  36. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/cloudtrail.py +0 -0
  37. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/cognito.py +0 -0
  38. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/config.py +0 -0
  39. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/controltower.py +0 -0
  40. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/cost.py +0 -0
  41. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/dynamo.py +0 -0
  42. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/ec2.py +0 -0
  43. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/ecr.py +0 -0
  44. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/ecs.py +0 -0
  45. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/efs.py +0 -0
  46. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/eks.py +0 -0
  47. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/elasticbeanstalk.py +0 -0
  48. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/elb.py +0 -0
  49. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/eventbridge.py +0 -0
  50. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/fsx.py +0 -0
  51. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/glue.py +0 -0
  52. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/guardduty.py +0 -0
  53. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/iam.py +0 -0
  54. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/inspector.py +0 -0
  55. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/kms.py +0 -0
  56. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/lambda_.py +0 -0
  57. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/lex.py +0 -0
  58. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/lightsail.py +0 -0
  59. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/logs.py +0 -0
  60. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/marketplace.py +0 -0
  61. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/polly.py +0 -0
  62. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/quick.py +0 -0
  63. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/rds.py +0 -0
  64. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/route53.py +0 -0
  65. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/s3.py +0 -0
  66. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/sagemaker.py +0 -0
  67. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/secretsmanager.py +0 -0
  68. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/sns.py +0 -0
  69. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/sqs.py +0 -0
  70. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/ssm.py +0 -0
  71. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/stepfunctions.py +0 -0
  72. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/storagegateway.py +0 -0
  73. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/support.py +0 -0
  74. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/resolvers/vpc.py +0 -0
  75. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/session_cache.py +0 -0
  76. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/src/nate2_scripts/console/types.py +0 -0
  77. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/__init__.py +0 -0
  78. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/__init__.py +0 -0
  79. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/apigateway.py +0 -0
  80. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/artifact.py +0 -0
  81. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/athena.py +0 -0
  82. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/batch.py +0 -0
  83. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/bedrock.py +0 -0
  84. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/cloudformation.py +0 -0
  85. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/cloudfront.py +0 -0
  86. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/cloudhsm.py +0 -0
  87. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/cloudtrail.py +0 -0
  88. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/cognito.py +0 -0
  89. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/config.py +0 -0
  90. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/conftest.py +0 -0
  91. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/controltower.py +0 -0
  92. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/cost.py +0 -0
  93. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/dynamo.py +0 -0
  94. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/ec2.py +0 -0
  95. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/ecr.py +0 -0
  96. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/ecs.py +0 -0
  97. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/efs.py +0 -0
  98. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/eks.py +0 -0
  99. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/elasticbeanstalk.py +0 -0
  100. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/elb.py +0 -0
  101. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/eventbridge.py +0 -0
  102. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/fsx.py +0 -0
  103. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/glue.py +0 -0
  104. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/guardduty.py +0 -0
  105. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/iam.py +0 -0
  106. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/inspector.py +0 -0
  107. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/kms.py +0 -0
  108. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/lambda_.py +0 -0
  109. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/lex.py +0 -0
  110. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/lightsail.py +0 -0
  111. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/logs.py +0 -0
  112. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/marketplace.py +0 -0
  113. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/polly.py +0 -0
  114. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/rds.py +0 -0
  115. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/route53.py +0 -0
  116. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/s3.py +0 -0
  117. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/sagemaker.py +0 -0
  118. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/secretsmanager.py +0 -0
  119. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/sns.py +0 -0
  120. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/sqs.py +0 -0
  121. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/ssm.py +0 -0
  122. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/stepfunctions.py +0 -0
  123. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/storagegateway.py +0 -0
  124. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/support.py +0 -0
  125. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/resolvers/vpc.py +0 -0
  126. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/test_aws/config +0 -0
  127. {nate2_scripts-2.0.0 → nate2_scripts-2.0.2}/tests/console/test_aws/credentials +0 -0
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.5
2
+ Name: nate2-scripts
3
+ Version: 2.0.2
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
+ [![PyPI version](https://img.shields.io/pypi/v/nate2-scripts)](https://pypi.org/project/nate2-scripts/)
17
+ [![License](https://img.shields.io/github/license/tennex-nwestfall/nate2-scripts)](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.0"
7
+ version = "2.0.2"
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",
@@ -54,7 +54,15 @@ def ensure_profile():
54
54
  profile_name = os.environ.get("AWS_PROFILE", "")
55
55
  if not profile_name:
56
56
  print(
57
- "Error: AWS_PROFILE environment variable is not set. run asp.",
57
+ "Error: AWS_PROFILE environment variable is not set. Run asp.",
58
+ file=sys.stderr,
59
+ )
60
+ sys.exit(1)
61
+
62
+ profile_region = os.environ.get("AWS_PROFILE_REGION", "")
63
+ if not profile_region:
64
+ print(
65
+ "Error: AWS_PROFILE_REGION environment variable is not set. Run asr or fix your .aws/config.",
58
66
  file=sys.stderr,
59
67
  )
60
68
  sys.exit(1)
@@ -194,7 +202,7 @@ def main() -> None:
194
202
  print("Default region is: ", session.region_name)
195
203
 
196
204
  suffix, context = get_suffix(args.service, args.search, args.region, session)
197
- if suffix:
205
+ if suffix is not None:
198
206
  open_console(suffix, context, session_cache)
199
207
  else:
200
208
  print("Cannot open console page.")
@@ -0,0 +1,11 @@
1
+ from nate2_scripts.console.resolvers import Context, Resolver
2
+
3
+
4
+ class BackupResolver(Resolver):
5
+ def get_service_names(self) -> list[str]:
6
+ return ["backup", "bp"]
7
+
8
+ def try_resolve_service(
9
+ self, context: Context, service: str, search: str
10
+ ) -> str | None:
11
+ return "/backup/home#/dashboard/activity/backup/"
@@ -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"
@@ -6,6 +6,7 @@ from nate2_scripts.console.resolvers import Resolver
6
6
  from nate2_scripts.console.resolvers.apigateway import ApiGatewayResolver
7
7
  from nate2_scripts.console.resolvers.artifact import ArtifactResolver
8
8
  from nate2_scripts.console.resolvers.athena import AthenaResolver
9
+ from nate2_scripts.console.resolvers.backup import BackupResolver
9
10
  from nate2_scripts.console.resolvers.batch import BatchResolver
10
11
  from nate2_scripts.console.resolvers.bedrock import BedrockResolver
11
12
  from nate2_scripts.console.resolvers.cloudformation import CloudFormationResolver
@@ -16,6 +17,7 @@ from nate2_scripts.console.resolvers.cognito import CognitoResolver
16
17
  from nate2_scripts.console.resolvers.config import ConfigResolver
17
18
  from nate2_scripts.console.resolvers.controltower import ControlTowerResolver
18
19
  from nate2_scripts.console.resolvers.cost import CostResolver
20
+ from nate2_scripts.console.resolvers.datasync import DataSyncResolver
19
21
  from nate2_scripts.console.resolvers.dynamo import DynamoResolver
20
22
  from nate2_scripts.console.resolvers.ec2 import Ec2Resolver
21
23
  from nate2_scripts.console.resolvers.ecr import EcrResolver
@@ -41,6 +43,7 @@ from nate2_scripts.console.resolvers.rds import RdsResolver
41
43
  from nate2_scripts.console.resolvers.route53 import Route53Resolver
42
44
  from nate2_scripts.console.resolvers.s3 import S3Resolver
43
45
  from nate2_scripts.console.resolvers.sagemaker import SagemakerResolver
46
+ from nate2_scripts.console.resolvers.scheduler import SchedulerResolver
44
47
  from nate2_scripts.console.resolvers.secretsmanager import SecretsManagerResolver
45
48
  from nate2_scripts.console.resolvers.sns import SnsResolver
46
49
  from nate2_scripts.console.resolvers.sqs import SqsResolver
@@ -64,6 +67,7 @@ RESOLVERS: list[Resolver] = [
64
67
  ApiGatewayResolver(),
65
68
  ArtifactResolver(),
66
69
  AthenaResolver(),
70
+ BackupResolver(),
67
71
  BatchResolver(),
68
72
  BedrockResolver(),
69
73
  CloudFrontResolver(),
@@ -73,6 +77,7 @@ RESOLVERS: list[Resolver] = [
73
77
  ConfigResolver(),
74
78
  ControlTowerResolver(),
75
79
  CostResolver(),
80
+ DataSyncResolver(),
76
81
  DynamoResolver(),
77
82
  EcrResolver(),
78
83
  EcsResolver(),
@@ -94,6 +99,7 @@ RESOLVERS: list[Resolver] = [
94
99
  RdsResolver(),
95
100
  Route53Resolver(),
96
101
  SagemakerResolver(),
102
+ SchedulerResolver(),
97
103
  SecretsManagerResolver(),
98
104
  SnsResolver(),
99
105
  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,36 @@
1
+ from nate2_scripts.console import get_suffix
2
+ from tests.console.resolvers import make_session
3
+
4
+ BACKUP_CONSOLE_URL = "/backup/home#/dashboard/activity/backup/"
5
+
6
+ # --------------------------------
7
+ # Service
8
+ # --------------------------------
9
+
10
+
11
+ def test_service_backup():
12
+ session = make_session("fake-A-org")
13
+
14
+ suffix, context = get_suffix(
15
+ "backup",
16
+ "this parameter doesn't matter",
17
+ "",
18
+ session,
19
+ )
20
+ assert suffix == BACKUP_CONSOLE_URL
21
+ assert context.session.region_name == "us-east-1"
22
+ assert context.identity["Account"] == "111111111111"
23
+
24
+
25
+ def test_service_bp():
26
+ session = make_session("fake-A-org")
27
+
28
+ suffix, context = get_suffix(
29
+ "bp",
30
+ "this parameter doesn't matter",
31
+ "",
32
+ session,
33
+ )
34
+ assert suffix == BACKUP_CONSOLE_URL
35
+ assert context.session.region_name == "us-east-1"
36
+ 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.
@@ -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'