awsmount 1.0.0__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 (95) hide show
  1. awsmount-1.0.0/LICENSE +21 -0
  2. awsmount-1.0.0/PKG-INFO +624 -0
  3. awsmount-1.0.0/README.md +584 -0
  4. awsmount-1.0.0/pyproject.toml +68 -0
  5. awsmount-1.0.0/setup.cfg +4 -0
  6. awsmount-1.0.0/src/awsmount/__init__.py +9 -0
  7. awsmount-1.0.0/src/awsmount/__main__.py +8 -0
  8. awsmount-1.0.0/src/awsmount/adapters/__init__.py +10 -0
  9. awsmount-1.0.0/src/awsmount/adapters/export.py +303 -0
  10. awsmount-1.0.0/src/awsmount/adapters/fuse/__init__.py +30 -0
  11. awsmount-1.0.0/src/awsmount/adapters/fuse/mount.py +163 -0
  12. awsmount-1.0.0/src/awsmount/adapters/fuse/operations.py +359 -0
  13. awsmount-1.0.0/src/awsmount/adapters/shell/__init__.py +13 -0
  14. awsmount-1.0.0/src/awsmount/adapters/shell/commands.py +966 -0
  15. awsmount-1.0.0/src/awsmount/adapters/shell/completer.py +177 -0
  16. awsmount-1.0.0/src/awsmount/adapters/shell/formatting.py +182 -0
  17. awsmount-1.0.0/src/awsmount/adapters/shell/jsonpath.py +98 -0
  18. awsmount-1.0.0/src/awsmount/adapters/shell/pipes.py +97 -0
  19. awsmount-1.0.0/src/awsmount/adapters/shell/repl.py +103 -0
  20. awsmount-1.0.0/src/awsmount/adapters/webdav/__init__.py +26 -0
  21. awsmount-1.0.0/src/awsmount/adapters/webdav/auth.py +76 -0
  22. awsmount-1.0.0/src/awsmount/adapters/webdav/macos.py +367 -0
  23. awsmount-1.0.0/src/awsmount/adapters/webdav/provider.py +297 -0
  24. awsmount-1.0.0/src/awsmount/adapters/webdav/server.py +376 -0
  25. awsmount-1.0.0/src/awsmount/cli.py +644 -0
  26. awsmount-1.0.0/src/awsmount/config.py +115 -0
  27. awsmount-1.0.0/src/awsmount/doctor.py +279 -0
  28. awsmount-1.0.0/src/awsmount/errors.py +117 -0
  29. awsmount-1.0.0/src/awsmount/platform.py +186 -0
  30. awsmount-1.0.0/src/awsmount/providers/__init__.py +292 -0
  31. awsmount-1.0.0/src/awsmount/providers/acm.py +112 -0
  32. awsmount-1.0.0/src/awsmount/providers/apigateway.py +126 -0
  33. awsmount-1.0.0/src/awsmount/providers/autoscaling.py +109 -0
  34. awsmount-1.0.0/src/awsmount/providers/base.py +177 -0
  35. awsmount-1.0.0/src/awsmount/providers/cfn_types.py +45 -0
  36. awsmount-1.0.0/src/awsmount/providers/cloudformation.py +148 -0
  37. awsmount-1.0.0/src/awsmount/providers/cloudfront.py +160 -0
  38. awsmount-1.0.0/src/awsmount/providers/cloudwatch.py +98 -0
  39. awsmount-1.0.0/src/awsmount/providers/common.py +168 -0
  40. awsmount-1.0.0/src/awsmount/providers/dynamodb.py +126 -0
  41. awsmount-1.0.0/src/awsmount/providers/ec2.py +418 -0
  42. awsmount-1.0.0/src/awsmount/providers/ecr.py +102 -0
  43. awsmount-1.0.0/src/awsmount/providers/ecs.py +244 -0
  44. awsmount-1.0.0/src/awsmount/providers/eks.py +272 -0
  45. awsmount-1.0.0/src/awsmount/providers/elbv2.py +367 -0
  46. awsmount-1.0.0/src/awsmount/providers/eventbridge.py +130 -0
  47. awsmount-1.0.0/src/awsmount/providers/iam.py +488 -0
  48. awsmount-1.0.0/src/awsmount/providers/kms.py +184 -0
  49. awsmount-1.0.0/src/awsmount/providers/lambda_.py +162 -0
  50. awsmount-1.0.0/src/awsmount/providers/logs.py +131 -0
  51. awsmount-1.0.0/src/awsmount/providers/organizations.py +141 -0
  52. awsmount-1.0.0/src/awsmount/providers/rds.py +201 -0
  53. awsmount-1.0.0/src/awsmount/providers/redshift.py +88 -0
  54. awsmount-1.0.0/src/awsmount/providers/refs.py +137 -0
  55. awsmount-1.0.0/src/awsmount/providers/route53.py +157 -0
  56. awsmount-1.0.0/src/awsmount/providers/s3.py +257 -0
  57. awsmount-1.0.0/src/awsmount/providers/secretsmanager.py +105 -0
  58. awsmount-1.0.0/src/awsmount/providers/sns.py +114 -0
  59. awsmount-1.0.0/src/awsmount/providers/sqs.py +129 -0
  60. awsmount-1.0.0/src/awsmount/providers/ssm.py +104 -0
  61. awsmount-1.0.0/src/awsmount/providers/stepfunctions.py +145 -0
  62. awsmount-1.0.0/src/awsmount/providers/vpc.py +573 -0
  63. awsmount-1.0.0/src/awsmount/secrets/__init__.py +5 -0
  64. awsmount-1.0.0/src/awsmount/secrets/cli.py +235 -0
  65. awsmount-1.0.0/src/awsmount/secrets/session.py +168 -0
  66. awsmount-1.0.0/src/awsmount/secrets/store.py +179 -0
  67. awsmount-1.0.0/src/awsmount/secrets/tree.py +382 -0
  68. awsmount-1.0.0/src/awsmount/session/__init__.py +3 -0
  69. awsmount-1.0.0/src/awsmount/session/accounts.py +245 -0
  70. awsmount-1.0.0/src/awsmount/session/budget.py +96 -0
  71. awsmount-1.0.0/src/awsmount/session/cache.py +188 -0
  72. awsmount-1.0.0/src/awsmount/session/clients.py +206 -0
  73. awsmount-1.0.0/src/awsmount/session/context.py +220 -0
  74. awsmount-1.0.0/src/awsmount/session/errorlog.py +229 -0
  75. awsmount-1.0.0/src/awsmount/session/limiter.py +59 -0
  76. awsmount-1.0.0/src/awsmount/session/progress.py +106 -0
  77. awsmount-1.0.0/src/awsmount/session/redact.py +259 -0
  78. awsmount-1.0.0/src/awsmount/tree/__init__.py +7 -0
  79. awsmount-1.0.0/src/awsmount/tree/collection.py +418 -0
  80. awsmount-1.0.0/src/awsmount/tree/links.py +187 -0
  81. awsmount-1.0.0/src/awsmount/tree/root.py +199 -0
  82. awsmount-1.0.0/src/awsmount/tree/tags.py +98 -0
  83. awsmount-1.0.0/src/awsmount/vfs/__init__.py +28 -0
  84. awsmount-1.0.0/src/awsmount/vfs/node.py +287 -0
  85. awsmount-1.0.0/src/awsmount/vfs/path.py +148 -0
  86. awsmount-1.0.0/src/awsmount/vfs/render.py +149 -0
  87. awsmount-1.0.0/src/awsmount/vfs/resolver.py +157 -0
  88. awsmount-1.0.0/src/awsmount/vfs/stat.py +107 -0
  89. awsmount-1.0.0/src/awsmount/vfs/walk.py +161 -0
  90. awsmount-1.0.0/src/awsmount.egg-info/PKG-INFO +624 -0
  91. awsmount-1.0.0/src/awsmount.egg-info/SOURCES.txt +93 -0
  92. awsmount-1.0.0/src/awsmount.egg-info/dependency_links.txt +1 -0
  93. awsmount-1.0.0/src/awsmount.egg-info/entry_points.txt +2 -0
  94. awsmount-1.0.0/src/awsmount.egg-info/requires.txt +12 -0
  95. awsmount-1.0.0/src/awsmount.egg-info/top_level.txt +1 -0
awsmount-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KloudSec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,624 @@
1
+ Metadata-Version: 2.4
2
+ Name: awsmount
3
+ Version: 1.0.0
4
+ Summary: Mount any AWS account as a read-only filesystem (shell, WebDAV, FUSE).
5
+ Author-email: Tarek CHEIKH <tarek@kloudsec.io>
6
+ Maintainer-email: Tarek CHEIKH <tarek@kloudsec.io>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/kloudsec-io/awsmount
9
+ Project-URL: Repository, https://github.com/kloudsec-io/awsmount
10
+ Project-URL: Issues, https://github.com/kloudsec-io/awsmount/issues
11
+ Project-URL: Documentation, https://github.com/kloudsec-io/awsmount#readme
12
+ Keywords: aws,filesystem,fuse,webdav,boto3,read-only
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: System :: Filesystems
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.11
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: boto3>=1.34
29
+ Requires-Dist: click>=8.1
30
+ Requires-Dist: prompt_toolkit>=3.0.40
31
+ Requires-Dist: wsgidav>=4.3
32
+ Requires-Dist: cheroot>=10
33
+ Requires-Dist: mfusepy>=1.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7; extra == "dev"
36
+ Requires-Dist: pytest-timeout>=2; extra == "dev"
37
+ Requires-Dist: moto[all,server]>=5; extra == "dev"
38
+ Requires-Dist: flake8>=6; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ <p align="center">
42
+ <img src="https://raw.githubusercontent.com/kloudsec-io/awsmount/main/assets/awsmount-logo.png" alt="awsmount logo" width="140">
43
+ </p>
44
+
45
+ <h1 align="center">awsmount</h1>
46
+
47
+ <p align="center">
48
+ <b>Mount any AWS account as a read-only filesystem.</b>
49
+ </p>
50
+
51
+ <p align="center">
52
+ <a href="https://pypi.org/project/awsmount/"><img src="https://img.shields.io/pypi/v/awsmount" alt="PyPI version"></a>
53
+ <a href="https://pypi.org/project/awsmount/"><img src="https://img.shields.io/pypi/pyversions/awsmount" alt="Python versions"></a>
54
+ <a href="https://github.com/kloudsec-io/awsmount/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"></a>
55
+ </p>
56
+
57
+ <p align="center">
58
+ <img src="https://raw.githubusercontent.com/kloudsec-io/awsmount/main/assets/awsmount-in-action.png" alt="awsmount mounts an AWS account as a read-only folder: ls, grep -r and cat work on it, secrets are hidden and writes are refused" width="820">
59
+ </p>
60
+
61
+ **awsmount turns your AWS account into a folder** that you can browse with the
62
+ tools you already know: `ls`, `cd`, `cat`, `grep -r`, `find`, `tree`,
63
+ `diff -r`, `jq` and `git`. Every resource (an EC2 instance, a security group,
64
+ a Lambda function, an IAM role...) is a directory, and every API response is
65
+ a readable `describe.json` file. It is **read-only**: nothing in AWS can be
66
+ changed through it.
67
+
68
+ It is for engineers who want to look around an account without clicking
69
+ through the console or remembering `aws ... describe-*` commands, and for AI
70
+ coding agents, which already know how to use a filesystem.
71
+
72
+ ## Why use it
73
+
74
+ - **Familiar tools.** `grep -rl '0.0.0.0/0' vpc/security-groups` finds every
75
+ rule open to the world. `cd` follows a resource to its VPC through relative
76
+ symlinks. `diff -r` compares two snapshots of an account.
77
+ - **Read-only.** Only `Describe*`, `List*` and `Get*` calls are allowed, and
78
+ that is enforced before any request is sent. Writes fail with "Read-only
79
+ file system".
80
+ - **Secrets hidden.** Lambda and ECS environment values, EC2 user data and
81
+ secret-looking CloudFormation values show as `<hidden>`. S3 object
82
+ contents and secret values are never fetched. (The separate command
83
+ [`awsmount secrets`](docs/secrets.md) is the only way to read secret
84
+ values, on purpose.)
85
+ - **Light on the API.** Nothing is fetched until you look at it, and an
86
+ optional call budget (`--max-api-calls`) can stop a runaway `grep -r`.
87
+
88
+ ## Install
89
+
90
+ awsmount needs **Python 3.11 or newer**, and AWS credentials set up the way
91
+ the AWS CLI expects (a named profile or environment variables).
92
+
93
+ ```
94
+ pipx install awsmount # recommended
95
+ # or: python3 -m pip install awsmount
96
+ # or, from source: pipx install git+https://github.com/kloudsec-io/awsmount
97
+ awsmount doctor
98
+ ```
99
+
100
+ `awsmount doctor` checks what works on your machine and ends with a summary
101
+ line such as:
102
+
103
+ ```
104
+ shell: OK · webdav-serve: OK · webdav-mount: OK · fuse: optional (not needed, WebDAV mount works)
105
+ ```
106
+
107
+ - **macOS:** nothing else to install. Mounting uses the WebDAV client that is
108
+ built into macOS, with no administrator password.
109
+ - **Linux:** the interactive shell needs nothing. Mounting needs FUSE:
110
+ `sudo apt install fuse3` (Debian, Ubuntu) or `sudo dnf install fuse3`
111
+ (Fedora).
112
+ - **From a checkout:** `python3 -m pip install -e .`
113
+
114
+ ## Getting started
115
+
116
+ Five minutes, from nothing to your first real answer. Replace `myprofile`
117
+ with your AWS profile (leave out `-p myprofile` to use your default
118
+ credentials) and `us-east-1` with the region you want to look at.
119
+
120
+ ### 1. Check that it works
121
+
122
+ ```
123
+ awsmount doctor --check-credentials -p myprofile
124
+ ```
125
+
126
+ The last lines show the AWS account and user it found. If a line says
127
+ `FAIL`, fix that first.
128
+
129
+ ### 2. Look around in the interactive shell
130
+
131
+ No mounting needed:
132
+
133
+ ```
134
+ awsmount -p myprofile -r us-east-1 shell
135
+ ```
136
+
137
+ You get `ls`, `cd`, `cat`, `grep`, `find`, `tree` and more (type `help`).
138
+ Below are commands you type in the shell and the output awsmount really
139
+ printed, captured from a throwaway test account. The account id and region
140
+ are shortened to `ACCT` and `REGION`, and the long `grep` result is cut
141
+ short (`...`):
142
+
143
+ ```
144
+ awsmount:/$ ls /
145
+ ACCT
146
+ awsmount:/$ ls /ACCT/REGION
147
+ acm
148
+ apigateway
149
+ autoscaling
150
+ cloudformation
151
+ cloudwatch
152
+ dynamodb
153
+ ec2
154
+ ecr
155
+ ecs
156
+ eks
157
+ elbv2
158
+ eventbridge
159
+ kms
160
+ lambda
161
+ logs
162
+ rds
163
+ redshift
164
+ s3
165
+ secretsmanager
166
+ sns
167
+ sqs
168
+ ssm
169
+ stepfunctions
170
+ vpc
171
+ awsmount:/$ ls /ACCT/REGION/ec2/instances/i-7960944c02c99b353
172
+ arn
173
+ describe.json
174
+ instance-type
175
+ links
176
+ name
177
+ private-ip
178
+ state
179
+ tags
180
+ tags.json
181
+ user-data
182
+ awsmount:/$ cat /ACCT/REGION/ec2/instances/i-7960944c02c99b353/user-data
183
+ <hidden>
184
+ awsmount:/$ grep -rl 0.0.0.0/0 /ACCT/REGION/vpc/security-groups
185
+ /ACCT/REGION/vpc/security-groups/sg-029ffec0a78d92776/describe.json
186
+ /ACCT/REGION/vpc/security-groups/sg-029ffec0a78d92776/rules.json
187
+ /ACCT/REGION/vpc/security-groups/sg-4f6d07da8cf2aaa65/describe.json
188
+ /ACCT/REGION/vpc/security-groups/sg-4f6d07da8cf2aaa65/rules.json
189
+ ...
190
+ awsmount:/$ cat /ACCT/REGION/cloudformation/stacks/leaktest/outputs.json
191
+ [
192
+ {
193
+ "OutputKey": "SecretKey",
194
+ "OutputValue": "<hidden>"
195
+ },
196
+ {
197
+ "OutputKey": "DbPass",
198
+ "OutputValue": "<hidden>"
199
+ },
200
+ {
201
+ "OutputKey": "QueueName",
202
+ "OutputValue": "ok-to-show"
203
+ }
204
+ ]
205
+ ```
206
+
207
+ The first level is the account (its alias, or its id), then the region, then
208
+ the service, then the kind of resource, then one directory per resource.
209
+ Note `user-data` showing `<hidden>`: awsmount hides secrets by default.
210
+
211
+ ### 3. Mount your account as a folder
212
+
213
+ ```
214
+ mkdir -p ~/aws
215
+ awsmount -p myprofile -r us-east-1 mount ~/aws
216
+ ```
217
+
218
+ Leave that terminal running. In a second terminal, use any tool you like:
219
+
220
+ ```
221
+ ls ~/aws # your account (alias or id)
222
+ cd ~/aws/<account>/us-east-1
223
+ ls # one folder per service
224
+ ls ec2/instances # one folder per instance
225
+ cat ec2/instances/i-0123456789abcdef0/name
226
+ grep -rl '0.0.0.0/0' vpc/security-groups # every rule open to the world
227
+ ```
228
+
229
+ Trying to change anything fails, on purpose:
230
+ `touch ~/aws/x` gives "Read-only file system".
231
+
232
+ ### 4. Unmount
233
+
234
+ Press Ctrl-C in the terminal that runs `awsmount mount`, or from anywhere:
235
+
236
+ ```
237
+ awsmount umount ~/aws
238
+ ```
239
+
240
+ ### 5. Where next
241
+
242
+ - Copy a [recipe](#recipes): find open security groups, see what uses a
243
+ resource, diff two snapshots, track an account in git, or hand the folder
244
+ to an AI coding agent.
245
+ - Read [what you will see](#what-you-will-see) to learn the layout.
246
+ - Check [Coverage](#coverage) for the services that are supported.
247
+
248
+ ## What you will see
249
+
250
+ ```
251
+ /<account-alias-or-id>/ one directory per profile/account
252
+ <region>/<service>/<collection>/<resource>/
253
+ global/{iam,route53,cloudfront,organizations}/...
254
+ _meta/account.json identity.json errors.log stats.json
255
+ ```
256
+
257
+ One resource looks like this (example names and IDs, not real output):
258
+
259
+ ```
260
+ /acme-prod/eu-west-1/vpc/security-groups/sg-0a1b2c3d4e5f60718/
261
+ describe.json arn name group-name vpc-id rules.json tags.json tags/
262
+ links/vpc -> ../../../vpcs/vpc-0abc1234def567890
263
+ referenced-by/ec2-instances-i-0123456789abcdef0
264
+ -> ../../../../ec2/instances/i-0123456789abcdef0
265
+ referenced-by/lambda-functions-orders-api
266
+ -> ../../../../lambda/functions/orders-api
267
+ ```
268
+
269
+ - Every resource directory has `describe.json`: the raw API data with
270
+ `ResponseMetadata` removed, ISO-8601 datetimes, sorted keys, indent 2.
271
+ - It also has `arn`, `name`, `tags.json` and `tags/<Key>` when they apply,
272
+ plus small files such as `state` or `runtime`, `links/` and, for security
273
+ groups, `referenced-by/`.
274
+ - Names containing `/`, `%`, control characters or a leading `.` are
275
+ `%XX`-escaped (`/aws/lambda/fn` becomes `%2Faws%2Flambda%2Ffn`).
276
+ Case-insensitive name collisions get a `~<id>` suffix.
277
+ - Several profiles (`-p a -p b`) give several top-level directories.
278
+ `--region all` mounts every region enabled for the account.
279
+
280
+ ## Recipes
281
+
282
+ > The account name `acme-prod`, region and resource IDs in the recipes below are **examples** to adapt, not captured output.
283
+
284
+ The examples assume a mount at `~/aws` with an account directory
285
+ `acme-prod`. Inside `awsmount shell` the same paths start at `/`.
286
+
287
+ ### Find security groups open to the world
288
+
289
+ `rules.json` is derived from the security group listing, so reading it costs
290
+ no extra API call. Each rule is one JSON object, one key per line:
291
+
292
+ ```
293
+ cd ~/aws/acme-prod/eu-west-1/vpc/security-groups
294
+ grep -l '"cidr": "0.0.0.0/0"' */rules.json # any direction
295
+ for f in */rules.json; do
296
+ jq -e 'any(.[]; .direction == "ingress"
297
+ and (.cidr == "0.0.0.0/0" or .cidr == "::/0"))' \
298
+ "$f" >/dev/null && echo "${f%/rules.json} $(cat "${f%/rules.json}/group-name")"
299
+ done
300
+ ```
301
+
302
+ Without a mount:
303
+
304
+ ```
305
+ awsmount -p prod -r eu-west-1 grep -r -l '"cidr": "0.0.0.0/0"' \
306
+ /acme-prod/eu-west-1/vpc/security-groups
307
+ ```
308
+
309
+ ### What uses this security group?
310
+
311
+ `referenced-by/` lists symlinks to every network interface, EC2 instance,
312
+ security group rule, load balancer, Lambda function, RDS instance and VPC
313
+ endpoint in the same account and region that uses the group:
314
+
315
+ ```
316
+ $ ls ~/aws/acme-prod/eu-west-1/vpc/security-groups/sg-0a1b2c3d4e5f60718/referenced-by
317
+ ec2-instances-i-0123456789abcdef0
318
+ ec2-network-interfaces-eni-0fedcba9876543210
319
+ lambda-functions-orders-api
320
+ vpc-security-groups-sg-0f9e8d7c6b5a49382
321
+ $ cat ~/aws/acme-prod/eu-west-1/vpc/security-groups/sg-0a1b2c3d4e5f60718/referenced-by/*/name
322
+ ```
323
+
324
+ On FUSE, `readlink` shows the targets. On a macOS WebDAV mount each entry is
325
+ a text file that contains the relative target, so `cat` the entry to see it.
326
+ Building the index costs one cached list call per source collection.
327
+
328
+ ### Diff two snapshots of an account
329
+
330
+ ```
331
+ awsmount -p prod -r eu-west-1 export /acme-prod/eu-west-1 ~/snap/monday
332
+ # ... later ...
333
+ awsmount -p prod -r eu-west-1 export /acme-prod/eu-west-1 ~/snap/tuesday
334
+ diff -r -x .awsmount-export ~/snap/monday ~/snap/tuesday
335
+ ```
336
+
337
+ By default `export` writes each symlink as a small text file holding its
338
+ target (`--links file`), so `diff -r` and `grep -R` work. With
339
+ `--links keep` the export contains real symlinks, and the links between
340
+ resources form cycles that make macOS `diff -r` report "Directory loop
341
+ detected".
342
+
343
+ Every export writes a `.awsmount-export` marker with the export time, so
344
+ exclude it from the diff. If you export a whole account directory, also
345
+ exclude `_meta` (`-x _meta`), because `stats.json` and `errors.log` change
346
+ from run to run. Export reads every file, lazy ones included, so it makes
347
+ many API calls: narrow it with `--services`.
348
+
349
+ ### Track an account in git
350
+
351
+ `--clean` deletes everything in the destination before writing (it requires
352
+ the marker of a previous export, or an empty or missing directory). Point it
353
+ at a subdirectory of the repository, never at the repository root, or it
354
+ would delete `.git`:
355
+
356
+ ```
357
+ mkdir -p ~/aws-history && cd ~/aws-history && git init
358
+ awsmount -p prod -r eu-west-1 --services vpc,ec2,elbv2,iam \
359
+ export /acme-prod ~/aws-history/acme-prod --clean
360
+ git add -A && git commit -m "acme-prod snapshot $(date -u +%F)"
361
+ ```
362
+
363
+ Run the last two lines on a schedule; `git log -p` then shows how the
364
+ account changed. Content is deterministic: AWS returns some lists in a
365
+ different order from one call to the next (tags, route table associations,
366
+ security group lists, IP permissions), so awsmount sorts exactly those, and
367
+ leaves every other list in API order. Masking stays on unless you pass
368
+ `--reveal`.
369
+
370
+ ### Use it with AI coding agents
371
+
372
+ Agents already know `ls`, `cat`, `grep`, `find` and `jq`. Give them a mount,
373
+ or let them run one-shot commands:
374
+
375
+ ```
376
+ awsmount -p prod -r eu-west-1 --max-api-calls 1000 mount ~/aws
377
+ # then, in the agent's instructions:
378
+ # "The AWS account is mounted read-only at ~/aws. Each resource is a
379
+ # directory with describe.json. Follow links/ and referenced-by/.
380
+ # Check ~/aws/<account>/_meta/errors.log for anything you could not read."
381
+ ```
382
+
383
+ ```
384
+ awsmount -p prod -r eu-west-1 shell -c "grep -r -l orders-api /acme-prod/eu-west-1/lambda"
385
+ ```
386
+
387
+ Why this is reasonably safe: no operation can modify AWS; secret values,
388
+ object bodies and log events are never fetched by `awsmount mount`, `shell`
389
+ and `serve`; secrets in metadata are
390
+ hidden; the budget stops runaway exploration; GNU `grep -r` and `find` do
391
+ not follow symlinks, and WebDAV mounts serve links as files, so recursive
392
+ tools cannot loop. Do **not** combine agents with `--reveal`: any local
393
+ process, the agent included, can read everything that is revealed.
394
+
395
+ ## Secret values: `awsmount secrets`
396
+
397
+ To read the **values** of Secrets Manager secrets and Parameter Store
398
+ parameters as files, there is one separate command. It is the only place
399
+ where awsmount reads a secret value:
400
+
401
+ ```
402
+ awsmount secrets mount ~/secrets -p prod -r eu-west-1
403
+ cat ~/secrets/secretsmanager/team%2Fapi/value # the secret, exactly
404
+ cat ~/secrets/ssm/app/db/password # decrypted SecureString
405
+ awsmount secrets umount ~/secrets
406
+ ```
407
+
408
+ Listing never reads a value; a value is fetched when its file is read, kept
409
+ in memory for 5 minutes (`--ttl`) and overwritten when it expires. One
410
+ profile and one region per mount. `awsmount mount`, `shell`, `serve` and
411
+ `export` still cannot read a value, even with `--reveal`.
412
+
413
+ Two limits you should know: overwriting memory is best effort, and on macOS
414
+ the system's WebDAV client keeps a temporary copy of each file it reads in
415
+ `/private/tmp` until you unmount (keep FileVault on). Read
416
+ [docs/secrets.md](docs/secrets.md) before using it.
417
+
418
+ ## Mounting on each OS
419
+
420
+ | | Interactive shell | Real mount | Serve over WebDAV |
421
+ |---|---|---|---|
422
+ | macOS | `awsmount shell` | `awsmount mount DIR`: WebDAV through the built-in `/sbin/mount_webdav` (zero install, no admin rights); FUSE only with `--method fuse` | `awsmount serve` |
423
+ | Linux | `awsmount shell` | `awsmount mount DIR`: FUSE through mfusepy and libfuse 2/3 | `awsmount serve` (mount it yourself, e.g. davfs2) |
424
+
425
+ `--method auto` (the default) picks FUSE on Linux and WebDAV on macOS, even
426
+ when FUSE-T or macFUSE happens to be installed.
427
+
428
+ - **macOS (WebDAV).** awsmount starts a WebDAV server on `127.0.0.1` only,
429
+ with a fresh random password per session, and mounts it read-only with the
430
+ system's own `mount_webdav`. WebDAV has no symlinks, so each link is served
431
+ as a small text file holding its target. Details, including how the
432
+ password is passed and why Spotlight is switched off for the mount:
433
+ [docs/mounting-macos.md](docs/mounting-macos.md).
434
+ - **Linux (FUSE).** The mount is read-only, never uses `allow_other` (only
435
+ your user can read it) and has real symlinks. Details:
436
+ [docs/mounting-linux.md](docs/mounting-linux.md).
437
+
438
+ ## Safety
439
+
440
+ - **Read-only.** Each boto3 client has a `before-call` hook that refuses any
441
+ operation that is not on the allow-list derived from the providers (each
442
+ operation is allowed only for the client whose provider declared it),
443
+ does not start with `Describe`/`List`/`Get`/`Search`/`Lookup`, or is on
444
+ the deny-list. The deny-list holds data-plane and credential operations:
445
+ `GetSecretValue`, `GetParameter*`, `GetObject`, `Decrypt`, `GetFunction`,
446
+ `GetLogEvents`, `GetPasswordData`, `ExportCertificate` and others. Only
447
+ `awsmount secrets` runs a separate session that may call `GetSecretValue`
448
+ and `GetParameter`, and nothing else. FUSE write operations return
449
+ `EROFS`. WebDAV write methods return 403.
450
+ - **Masking.** It is applied at render time, to every JSON file, and also
451
+ to exports. EC2 `user-data` and CloudFormation `template` are not even
452
+ fetched unless you pass `--reveal`. Tag values are not hidden.
453
+ - **`--reveal` risk.** `--reveal` turns masking off and prints a warning.
454
+ Mounted files are readable by any process running as your user, including
455
+ AI agents, editors, indexers and backup tools. Use it only for a
456
+ short-lived, targeted session.
457
+ - **Other local users.** The FUSE mount has no `allow_other`. The WebDAV
458
+ server binds to 127.0.0.1 only, checks the `Host` header (DNS-rebinding
459
+ defence), and requires HTTP auth on every request. The per-session URL
460
+ token is not a secret (on macOS it is visible in `ps` and `mount`
461
+ output); the password is the secret. The credentials are generated per
462
+ session and never written to disk. awsmount warns when another user could
463
+ modify the mountpoint's parent directory, or reach it from outside your
464
+ home directory.
465
+
466
+ Details: [docs/security.md](docs/security.md).
467
+
468
+ ## Coverage
469
+
470
+ Coverage today: 28 services, 63 resource types. awsmount does not cover
471
+ every AWS service. This table is the output of `awsmount services --short`:
472
+
473
+ | Service | Scope | Resource types |
474
+ |---|---|---|
475
+ | ec2 | region | instances, volumes, snapshots, key-pairs, elastic-ips, network-interfaces, amis, launch-templates |
476
+ | vpc | region | vpcs, subnets, security-groups, route-tables, internet-gateways, nat-gateways, network-acls, endpoints, peering-connections, dhcp-options, transit-gateways, flow-logs |
477
+ | elbv2 | region | load-balancers, load-balancers/listeners, load-balancers/listeners/rules, target-groups |
478
+ | lambda | region | functions |
479
+ | rds | region | instances, clusters, clusters/members |
480
+ | ecs | region | clusters, clusters/services, task-definitions |
481
+ | eks | region | clusters, clusters/nodegroups, clusters/addons |
482
+ | autoscaling | region | groups |
483
+ | s3 | region | buckets |
484
+ | dynamodb | region | tables |
485
+ | sqs | region | queues |
486
+ | sns | region | topics |
487
+ | kms | region | keys, aliases |
488
+ | logs | region | log-groups |
489
+ | cloudwatch | region | alarms |
490
+ | ecr | region | repositories |
491
+ | stepfunctions | region | state-machines |
492
+ | redshift | region | clusters |
493
+ | eventbridge | region | event-buses, event-buses/rules |
494
+ | cloudformation | region | stacks, stacks/resources |
495
+ | secretsmanager | region | secrets |
496
+ | ssm | region | parameters |
497
+ | acm | region | certificates |
498
+ | route53 | global | hosted-zones |
499
+ | iam | global | users, roles, groups, policies |
500
+ | cloudfront | global | distributions |
501
+ | apigateway | region | rest-apis |
502
+ | organizations | global | accounts, roots, policies |
503
+
504
+ `awsmount services` (without `--short`) also lists the files and links
505
+ inside each resource directory, so you can find a path without reading the
506
+ docs. `awsmount services --markdown` regenerates
507
+ [docs/coverage.md](docs/coverage.md), which has that list as a table, and
508
+ `--json` gives machine-readable output.
509
+
510
+ ## Verification status
511
+
512
+ - **Automated tests** run against the moto AWS emulator: every provider, the
513
+ read-only guard, masking, caching, the shell, the WebDAV server and the
514
+ FUSE operations.
515
+ - **Real mounts** are tested by scripts: `tests/e2e/macos_webdav.sh` (macOS's
516
+ built-in WebDAV client) and `tests/e2e/linux_fuse.sh` (kernel FUSE in
517
+ Docker), plus `tests/e2e/secrets_macos_webdav.sh` and
518
+ `tests/e2e/secrets_linux_fuse.sh` for `awsmount secrets`.
519
+ - **Real AWS:** checked on a sandbox account, from a Mac and from an Amazon
520
+ Linux 2023 instance, against the AWS API. This includes `awsmount secrets`.
521
+ - **Not tested on real AWS:** CloudFront, Organizations data, cross-account
522
+ trust, an account with thousands of resources (throttling, long
523
+ pagination) and most non-admin permission sets. moto also lacks several of
524
+ these APIs, so those paths are covered by unit tests only. Treat 1.0.0 as
525
+ ready for evaluation on non-production accounts.
526
+ - **Not covered:** Windows, and mounting the macOS tree through FUSE-T or
527
+ macFUSE (the code path exists but has not been run, and awsmount does not
528
+ recommend it; see [docs/mounting-macos.md](docs/mounting-macos.md)).
529
+
530
+ ## Limits
531
+
532
+ - 28 services and 63 resource types; everything else is absent. EC2
533
+ snapshots and AMIs are limited to those you own, and ECS task definitions
534
+ to ACTIVE ones.
535
+ - Data is cached for `--ttl` seconds (60 by default). File mtimes are fetch
536
+ times. The shell's `refresh` command drops the cache; a mounted tree
537
+ refreshes when the TTL expires.
538
+ - Lazy files (policies, tags and similar) are fetched when read. In
539
+ `awsmount shell`, `ls -l` renders them to show real sizes (a few API calls
540
+ per directory, cached for the TTL). The FUSE mount (default `--lazy-size
541
+ zero`) and the macOS WebDAV mount report a size of 0 until the file is
542
+ read, so `ls -l` makes no API calls there; the content is still complete.
543
+ Use `--lazy-size render` on FUSE for real sizes. Collection directories
544
+ only contain directories, so listing them is cheap.
545
+ - Walking a whole account (`find`, `tree`, `du`) lists every directory, and
546
+ some directory names come from AWS: each IAM role's `tags/`,
547
+ `inline-policies/` and `links/`, log group and KMS key tags, S3 bucket
548
+ locations, ECS task definitions. On a small account that is a few hundred
549
+ calls; with hundreds of IAM roles it is thousands, one after another.
550
+ Narrow the walk (`find ~/aws/prod/us-east-1/vpc ...`) or mount fewer
551
+ services (`--services vpc,ec2,s3`).
552
+ - `grep -r` over a whole account reads every lazy file, which can take a
553
+ long time. There is no call limit by default (AWS only throttles per API
554
+ and boto3 backs off). Narrow the search with `--services` or `--region`, or
555
+ set `--max-api-calls N` for a hard stop: uncached reads then fail with
556
+ `EIO` and "API call budget exhausted", and cached data is still served.
557
+ - On macOS WebDAV mounts symlinks are text files by default.
558
+ `--webdav-links follow` serves them as their targets, but recursive tools
559
+ may then loop.
560
+ - Linux has no automated WebDAV mount; use FUSE, or `awsmount serve` with
561
+ your own WebDAV client.
562
+ - The region directory lists only that region's buckets; use `--region all`
563
+ to list all of them.
564
+ - S3 object keys are listed only with `--s3-objects N`, and then only the
565
+ first N keys per bucket. Object bodies, secret values, SSM parameter
566
+ values, log events and Lambda code are never available, even with
567
+ `--reveal`. (Secret and parameter values are available only through
568
+ `awsmount secrets`.)
569
+ - Tag values are never hidden.
570
+ - `referenced-by/` exists for security groups only. Its sources are EC2
571
+ instances, network interfaces, other security groups' rules, load
572
+ balancers, Lambda functions, RDS instances and VPC endpoints, in the same
573
+ account and region. ECS services and EKS clusters are not sources: EKS
574
+ lists only cluster names, so finding their security groups would cost one
575
+ extra call per cluster.
576
+ - Not covered inside the services above: EC2 launch template versions
577
+ (they hold user data), ECR images, Step Functions executions, EventBridge
578
+ events and connections, Redshift Serverless, and legacy Auto Scaling
579
+ launch configurations.
580
+ - Names that contain `/` are shown percent-encoded, so the CloudWatch log
581
+ group `/aws/lambda/fn` is the directory `%2Faws%2Flambda%2Ffn`.
582
+ - Resources AWS still reports in a terminal state (terminated EC2
583
+ instances, deleted NAT gateways, Transit Gateways, VPC endpoints and
584
+ peering connections) are not listed.
585
+ - The shell's built-in `grep` uses Python regular expressions.
586
+ - Organizations data appears only in an account allowed to read it, usually
587
+ the management account. Elsewhere the directory is empty and an entry
588
+ goes to `_meta/errors.log`.
589
+ - AccessDenied on a service gives an empty directory plus an `errors.log`
590
+ entry; AccessDenied on a lazy file gives `EACCES` ("Permission denied")
591
+ on both FUSE and the macOS mount, and the cause is a line in
592
+ `_meta/errors.log`. "Operation not permitted" (`EPERM`) is reserved for
593
+ the read-only guard refusing a call, which is a bug. With
594
+ `awsmount serve` a WebDAV client sees HTTP 403 for both. Gaps in moto,
595
+ localemu or other endpoints show up the same way.
596
+ - macOS and Linux only. Windows is not supported. On Windows you can try
597
+ WSL 2 (a Linux distribution such as Ubuntu) and follow the Linux
598
+ instructions; this has not been tested.
599
+
600
+ ## Documentation
601
+
602
+ - [docs/usage.md](docs/usage.md): commands, options, layout, recipes
603
+ - [docs/security.md](docs/security.md): read-only model, masking,
604
+ `--reveal`, authentication
605
+ - [docs/secrets.md](docs/secrets.md): `awsmount secrets`, the only command
606
+ that reads secret values
607
+ - [docs/mounting-macos.md](docs/mounting-macos.md): WebDAV, FUSE-T/macFUSE,
608
+ Spotlight
609
+ - [docs/mounting-linux.md](docs/mounting-linux.md): fuse3, fusermount,
610
+ Docker
611
+ - [docs/providers.md](docs/providers.md): how to write a provider
612
+ - [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): how the code is built (layers,
613
+ the read-only guard, how directories are made, the adapters)
614
+ - [docs/coverage.md](docs/coverage.md): generated coverage table, with the
615
+ files and links of every resource type
616
+ - [SECURITY.md](SECURITY.md): how to report a vulnerability
617
+ - [CONTRIBUTING.md](CONTRIBUTING.md): how to report a problem or send a
618
+ change
619
+
620
+ ## License
621
+
622
+ MIT
623
+
624
+ Maintained by [KloudSec](https://kloudsec.io).