apx-aws-lambda 0.1.0__tar.gz → 0.2.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 (30) hide show
  1. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/PKG-INFO +37 -2
  2. apx_aws_lambda-0.2.0/README.md +75 -0
  3. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/lambda_.py +89 -8
  4. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/plugin.py +1 -1
  5. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/shell.py +7 -2
  6. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/PKG-INFO +37 -2
  7. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/pyproject.toml +1 -1
  8. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/tests/test_targets.py +48 -0
  9. apx_aws_lambda-0.1.0/README.md +0 -40
  10. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/LICENSE +0 -0
  11. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/__init__.py +0 -0
  12. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/cli.py +0 -0
  13. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/cookiecutter.json +0 -0
  14. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/hooks/post_gen_project.py +0 -0
  15. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/.github/workflows/deploy.yml +0 -0
  16. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/DEPLOY.md +0 -0
  17. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/_lang/python/Makefile +0 -0
  18. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/_lang/python/lambda_handler.py +0 -0
  19. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/requirements/policy.json +0 -0
  20. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/requirements/trust.json +0 -0
  21. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/samconfig.toml +0 -0
  22. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/cloud/aws/lambda/{{cookiecutter.project_slug}}/template.yaml +0 -0
  23. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/overlays/index.json +0 -0
  24. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda/tools.py +0 -0
  25. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/SOURCES.txt +0 -0
  26. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/dependency_links.txt +0 -0
  27. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/entry_points.txt +0 -0
  28. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/requires.txt +0 -0
  29. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/apx_aws_lambda.egg-info/top_level.txt +0 -0
  30. {apx_aws_lambda-0.1.0 → apx_aws_lambda-0.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apx-aws-lambda
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: AWS Lambda for Action Platform: the aws/lambda deploy target (SAM) with its overlay, and tools to read stacks and functions.
5
5
  Author-email: Action Platform <cloud@actionplatform.io>
6
6
  License-Expression: Apache-2.0
@@ -42,9 +42,44 @@ target = "aws/lambda"
42
42
  region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
43
43
  ```
44
44
 
45
+ ## Credentials without keys
46
+
47
+ Set `role_arn` and nothing else: the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`). No access key on the platform, on your machine or in the repository; what the app may do is the role's policy.
48
+
49
+ ```toml
50
+ [deploy]
51
+ target = "aws/lambda"
52
+ role_arn = "arn:aws:iam::123456789012:role/shop-deploy"
53
+ ```
54
+
55
+ Once per AWS account, register the platform as an identity provider — its discovery document is `https://<platform>/.well-known/openid-configuration`:
56
+
57
+ ```bash
58
+ aws iam create-open-id-connect-provider --url https://platform.example.com --client-id-list sts.amazonaws.com
59
+ ```
60
+
61
+ Then a role per app (or per project) whose trust policy names the platform and the app; `requirements/policy.json` from the overlay is its permission policy:
62
+
63
+ ```json
64
+ {
65
+ "Version": "2012-10-17",
66
+ "Statement": [{
67
+ "Effect": "Allow",
68
+ "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/platform.example.com" },
69
+ "Action": "sts:AssumeRoleWithWebIdentity",
70
+ "Condition": {
71
+ "StringEquals": { "platform.example.com:aud": "sts.amazonaws.com" },
72
+ "StringLike": { "platform.example.com:sub": "org:acme:project:shop:app:orders" }
73
+ }
74
+ }]
75
+ }
76
+ ```
77
+
78
+ `sub` is `org:<org>:project:<project>:app:<app>` for a deploy the platform runs; `org:<org>` plus an `actor` claim for `action-platform deploy` from a logged-in machine (`StringLike` with `org:acme:*` covers both). Without `role_arn` the AWS CLI's own chain applies: `aws sso login`, a profile, an instance role — still no key in a file when you use SSO.
79
+
45
80
  Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
46
81
 
47
- Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), credentials in the environment (`AWS_PROFILE` or keys), `AWS_REGION`. Talks to `*.amazonaws.com` only.
82
+ Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), `AWS_REGION` (or `region` under `[deploy]`), and either `role_arn` (OIDC, no keys) or the AWS CLI's own credentials. Talks to `*.amazonaws.com` only.
48
83
 
49
84
  ## Development
50
85
 
@@ -0,0 +1,75 @@
1
+ # apx-aws-lambda
2
+
3
+ AWS Lambda for [Action Platform](https://github.com/actionplatform/action-platform): the `aws/lambda` deploy target (SAM) with its overlay, read-only tools and commands. `apx-` is the prefix every Action Platform extension carries.
4
+
5
+ ```bash
6
+ action-platform plugin install aws-lambda
7
+ action-platform cloud set aws/lambda # overlay files come from this plugin
8
+ action-platform deploy --dry-run # preflight: sam validate, credentials
9
+ action-platform deploy # sam build + sam deploy --config-env <stage>
10
+ action-platform diagnose
11
+ ```
12
+
13
+ | | |
14
+ |---|---|
15
+ | Overlay | `template.yaml`, `samconfig.toml`, `lambda_handler.py`, `Makefile`, `requirements/` (IAM), `.github/workflows/deploy.yml` |
16
+ | Deploy | `sam build` + `sam deploy --config-env <stage>` (`dev` → `default`, `prod` → `prod`) |
17
+ | Rollback | CloudFormation `rollback-stack` — previous stack state |
18
+ | Diagnose | stack status and the HTTP API url |
19
+ | Destroy | `sam delete` |
20
+
21
+ `[deploy]` in platform.toml:
22
+
23
+ ```toml
24
+ [deploy]
25
+ target = "aws/lambda"
26
+ region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
27
+ ```
28
+
29
+ ## Credentials without keys
30
+
31
+ Set `role_arn` and nothing else: the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`). No access key on the platform, on your machine or in the repository; what the app may do is the role's policy.
32
+
33
+ ```toml
34
+ [deploy]
35
+ target = "aws/lambda"
36
+ role_arn = "arn:aws:iam::123456789012:role/shop-deploy"
37
+ ```
38
+
39
+ Once per AWS account, register the platform as an identity provider — its discovery document is `https://<platform>/.well-known/openid-configuration`:
40
+
41
+ ```bash
42
+ aws iam create-open-id-connect-provider --url https://platform.example.com --client-id-list sts.amazonaws.com
43
+ ```
44
+
45
+ Then a role per app (or per project) whose trust policy names the platform and the app; `requirements/policy.json` from the overlay is its permission policy:
46
+
47
+ ```json
48
+ {
49
+ "Version": "2012-10-17",
50
+ "Statement": [{
51
+ "Effect": "Allow",
52
+ "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/platform.example.com" },
53
+ "Action": "sts:AssumeRoleWithWebIdentity",
54
+ "Condition": {
55
+ "StringEquals": { "platform.example.com:aud": "sts.amazonaws.com" },
56
+ "StringLike": { "platform.example.com:sub": "org:acme:project:shop:app:orders" }
57
+ }
58
+ }]
59
+ }
60
+ ```
61
+
62
+ `sub` is `org:<org>:project:<project>:app:<app>` for a deploy the platform runs; `org:<org>` plus an `actor` claim for `action-platform deploy` from a logged-in machine (`StringLike` with `org:acme:*` covers both). Without `role_arn` the AWS CLI's own chain applies: `aws sso login`, a profile, an instance role — still no key in a file when you use SSO.
63
+
64
+ Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
65
+
66
+ Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), `AWS_REGION` (or `region` under `[deploy]`), and either `role_arn` (OIDC, no keys) or the AWS CLI's own credentials. Talks to `*.amazonaws.com` only.
67
+
68
+ ## Development
69
+
70
+ ```bash
71
+ pip install -e ".[dev]"
72
+ pytest
73
+ ```
74
+
75
+ The tests fake `aws` and `sam`; nothing reaches AWS.
@@ -1,4 +1,6 @@
1
- """`aws/lambda`: a SAM stack per stage. Deploy = `sam build` + `sam deploy --config-env <stage>`; rollback = CloudFormation rollback to the previous stack state; diagnose = stack status and the HTTP API url."""
1
+ """`aws/lambda`: a SAM stack per stage. Deploy = `sam build` + `sam deploy --config-env <stage>`; rollback = CloudFormation rollback to the previous stack state; diagnose = stack status and the HTTP API url.
2
+
3
+ Credentials: with `role_arn` under `[deploy]`, the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`) — no access key anywhere. Without it, the AWS CLI's own chain applies (profile, SSO, instance role)."""
2
4
 
3
5
  from __future__ import annotations
4
6
 
@@ -7,7 +9,8 @@ import tomllib
7
9
 
8
10
  from action_platform.abc import DeployTarget
9
11
  from action_platform.core.context import Context, DeployResult, Diagnosis
10
- from action_platform.core.exception import DeployError
12
+ from action_platform.core.exception import ActionPlatformError, DeployError
13
+ from action_platform.remote.client import Remote
11
14
 
12
15
  from apx_aws_lambda import shell
13
16
 
@@ -16,7 +19,12 @@ class LambdaTarget(DeployTarget):
16
19
  name = "aws/lambda"
17
20
 
18
21
  def __init__(
19
- self, region: str | None = None, config: str = "samconfig.toml", **_: object
22
+ self,
23
+ region: str | None = None,
24
+ config: str = "samconfig.toml",
25
+ role_arn: str | None = None,
26
+ session_name: str = "action-platform",
27
+ **_: object,
20
28
  ) -> None:
21
29
  self.region = (
22
30
  region
@@ -24,6 +32,22 @@ class LambdaTarget(DeployTarget):
24
32
  or os.environ.get("AWS_DEFAULT_REGION")
25
33
  )
26
34
  self.config = config
35
+ self.role_arn = role_arn or os.environ.get("AWS_ROLE_ARN")
36
+ self.session_name = session_name
37
+ self._env: dict[str, str] | None = None
38
+
39
+ def env(self, ctx: Context) -> dict[str, str] | None:
40
+ """The environment `aws` and `sam` run with: temporary credentials from `role_arn` when set, the caller's own otherwise."""
41
+ if not self.role_arn:
42
+ return None
43
+
44
+ if self._env is None:
45
+ self._env = {
46
+ **os.environ,
47
+ **assume_role(ctx, self.role_arn, self.session_name, self.region),
48
+ }
49
+
50
+ return self._env
27
51
 
28
52
  def _stage(self, ctx: Context) -> str:
29
53
  return "prod" if ctx.stage == "prod" else "default"
@@ -72,19 +96,25 @@ class LambdaTarget(DeployTarget):
72
96
  )
73
97
 
74
98
  self._stack(ctx)
75
- shell.aws("sts", "get-caller-identity", region=self._region(ctx))
76
- shell.run([shell.require("sam", ""), "validate", "--lint"], cwd=ctx.repo_root)
99
+ shell.aws(
100
+ "sts", "get-caller-identity", region=self._region(ctx), env=self.env(ctx)
101
+ )
102
+ shell.run(
103
+ [shell.require("sam", ""), "validate", "--lint"],
104
+ cwd=ctx.repo_root,
105
+ env=self.env(ctx),
106
+ )
77
107
 
78
108
  def deploy(self, ctx: Context) -> DeployResult:
79
109
  sam = shell.require("sam", "pip install aws-sam-cli")
80
110
  stage = self._stage(ctx)
81
- shell.run([sam, "build"], cwd=ctx.repo_root)
111
+ shell.run([sam, "build"], cwd=ctx.repo_root, env=self.env(ctx))
82
112
  args = [sam, "deploy", "--no-confirm-changeset", "--no-fail-on-empty-changeset"]
83
113
 
84
114
  if stage != "default":
85
115
  args += ["--config-env", stage]
86
116
 
87
- shell.run(args, cwd=ctx.repo_root)
117
+ shell.run(args, cwd=ctx.repo_root, env=self.env(ctx))
88
118
  url = self._url(ctx)
89
119
 
90
120
  return DeployResult(
@@ -104,6 +134,7 @@ class LambdaTarget(DeployTarget):
104
134
  "--stack-name",
105
135
  self._stack(ctx),
106
136
  region=self._region(ctx),
137
+ env=self.env(ctx),
107
138
  )
108
139
 
109
140
  def diagnose(self, ctx: Context) -> Diagnosis:
@@ -116,6 +147,7 @@ class LambdaTarget(DeployTarget):
116
147
  "--stack-name",
117
148
  stack,
118
149
  region=self._region(ctx),
150
+ env=self.env(ctx),
119
151
  )
120
152
  except DeployError as e:
121
153
  return Diagnosis(
@@ -151,10 +183,59 @@ class LambdaTarget(DeployTarget):
151
183
  if region:
152
184
  args += ["--region", region]
153
185
 
154
- shell.run(args, cwd=ctx.repo_root)
186
+ shell.run(args, cwd=ctx.repo_root, env=self.env(ctx))
155
187
 
156
188
  def _url(self, ctx: Context) -> str | None:
157
189
  try:
158
190
  return self.diagnose(ctx).url
159
191
  except DeployError:
160
192
  return None
193
+
194
+
195
+ def assume_role(
196
+ ctx: Context, role_arn: str, session_name: str, region: str | None
197
+ ) -> dict[str, str]:
198
+ """Temporary credentials for `role_arn` from an OIDC token: the platform's for a deploy it runs, the platform the CLI is logged in to otherwise."""
199
+ token = ctx.identity_token("sts.amazonaws.com")
200
+
201
+ if token is None:
202
+ try:
203
+ token = Remote.from_credentials().identity_token("sts.amazonaws.com")[
204
+ "token"
205
+ ]
206
+ except ActionPlatformError as e:
207
+ raise DeployError(
208
+ f"role_arn is set but nothing can sign an identity token here: {e}. "
209
+ "Deploy from the platform, or log in with `action-platform login`."
210
+ ) from e
211
+
212
+ data = shell.aws(
213
+ "sts",
214
+ "assume-role-with-web-identity",
215
+ "--role-arn",
216
+ role_arn,
217
+ "--role-session-name",
218
+ session_name,
219
+ "--web-identity-token",
220
+ token,
221
+ "--duration-seconds",
222
+ "3600",
223
+ region=region,
224
+ env={
225
+ k: v
226
+ for k, v in os.environ.items()
227
+ if not k.startswith("AWS_ACCESS") and k != "AWS_SESSION_TOKEN"
228
+ },
229
+ )
230
+ creds = data.get("Credentials") or {}
231
+
232
+ if not creds.get("AccessKeyId"):
233
+ raise DeployError(
234
+ f"assume-role-with-web-identity gave no credentials for {role_arn}"
235
+ )
236
+
237
+ return {
238
+ "AWS_ACCESS_KEY_ID": creds["AccessKeyId"],
239
+ "AWS_SECRET_ACCESS_KEY": creds["SecretAccessKey"],
240
+ "AWS_SESSION_TOKEN": creds["SessionToken"],
241
+ }
@@ -18,7 +18,7 @@ class AwsLambdaPlugin(Plugin):
18
18
  min_core = "0.16"
19
19
  needs = [
20
20
  "tool: aws (AWS CLI v2), sam (AWS SAM CLI)",
21
- "env: AWS_PROFILE or AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY, AWS_REGION",
21
+ "env: AWS_REGION; credentials from role_arn (OIDC, no keys) or the AWS CLI chain (SSO, profile, instance role)",
22
22
  "net: *.amazonaws.com",
23
23
  ]
24
24
 
@@ -35,12 +35,17 @@ def run(
35
35
  return result.stdout
36
36
 
37
37
 
38
- def aws(*args: str, region: str | None = None, cwd: Path | None = None) -> Any:
38
+ def aws(
39
+ *args: str,
40
+ region: str | None = None,
41
+ cwd: Path | None = None,
42
+ env: dict[str, str] | None = None,
43
+ ) -> Any:
39
44
  command = [require("aws", "https://aws.amazon.com/cli/"), *args, "--output", "json"]
40
45
 
41
46
  if region:
42
47
  command += ["--region", region]
43
48
 
44
- out = run(command, cwd=cwd)
49
+ out = run(command, cwd=cwd, env=env)
45
50
 
46
51
  return json.loads(out) if out.strip() else {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apx-aws-lambda
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: AWS Lambda for Action Platform: the aws/lambda deploy target (SAM) with its overlay, and tools to read stacks and functions.
5
5
  Author-email: Action Platform <cloud@actionplatform.io>
6
6
  License-Expression: Apache-2.0
@@ -42,9 +42,44 @@ target = "aws/lambda"
42
42
  region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
43
43
  ```
44
44
 
45
+ ## Credentials without keys
46
+
47
+ Set `role_arn` and nothing else: the target assumes that role with a short-lived OIDC token the platform signs (`sts assume-role-with-web-identity`). No access key on the platform, on your machine or in the repository; what the app may do is the role's policy.
48
+
49
+ ```toml
50
+ [deploy]
51
+ target = "aws/lambda"
52
+ role_arn = "arn:aws:iam::123456789012:role/shop-deploy"
53
+ ```
54
+
55
+ Once per AWS account, register the platform as an identity provider — its discovery document is `https://<platform>/.well-known/openid-configuration`:
56
+
57
+ ```bash
58
+ aws iam create-open-id-connect-provider --url https://platform.example.com --client-id-list sts.amazonaws.com
59
+ ```
60
+
61
+ Then a role per app (or per project) whose trust policy names the platform and the app; `requirements/policy.json` from the overlay is its permission policy:
62
+
63
+ ```json
64
+ {
65
+ "Version": "2012-10-17",
66
+ "Statement": [{
67
+ "Effect": "Allow",
68
+ "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/platform.example.com" },
69
+ "Action": "sts:AssumeRoleWithWebIdentity",
70
+ "Condition": {
71
+ "StringEquals": { "platform.example.com:aud": "sts.amazonaws.com" },
72
+ "StringLike": { "platform.example.com:sub": "org:acme:project:shop:app:orders" }
73
+ }
74
+ }]
75
+ }
76
+ ```
77
+
78
+ `sub` is `org:<org>:project:<project>:app:<app>` for a deploy the platform runs; `org:<org>` plus an `actor` claim for `action-platform deploy` from a logged-in machine (`StringLike` with `org:acme:*` covers both). Without `role_arn` the AWS CLI's own chain applies: `aws sso login`, a profile, an instance role — still no key in a file when you use SSO.
79
+
45
80
  Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
46
81
 
47
- Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), credentials in the environment (`AWS_PROFILE` or keys), `AWS_REGION`. Talks to `*.amazonaws.com` only.
82
+ Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), `AWS_REGION` (or `region` under `[deploy]`), and either `role_arn` (OIDC, no keys) or the AWS CLI's own credentials. Talks to `*.amazonaws.com` only.
48
83
 
49
84
  ## Development
50
85
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "apx-aws-lambda"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "AWS Lambda for Action Platform: the aws/lambda deploy target (SAM) with its overlay, and tools to read stacks and functions."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -141,3 +141,51 @@ class PluginTest(unittest.TestCase):
141
141
  names = {t.name for t in asyncio.run(server.build().list_tools())}
142
142
 
143
143
  self.assertLessEqual({"aws_lambda_stacks", "aws_lambda_functions"}, names)
144
+
145
+
146
+ class AssumeRoleTest(unittest.TestCase):
147
+ def test_role_arn_turns_the_platform_token_into_temporary_credentials(self):
148
+ calls: list[list[str]] = []
149
+
150
+ def run(args, cwd=None, env=None):
151
+ calls.append([Path(args[0]).name, *args[1:]])
152
+ if args[1:3] == ["sts", "assume-role-with-web-identity"]:
153
+ return json.dumps(
154
+ {
155
+ "Credentials": {
156
+ "AccessKeyId": "AKIA",
157
+ "SecretAccessKey": "s",
158
+ "SessionToken": "t",
159
+ }
160
+ }
161
+ )
162
+ return json.dumps(
163
+ {"Stacks": [{"StackStatus": "UPDATE_COMPLETE", "Outputs": []}]}
164
+ )
165
+
166
+ ctx = Context(repo_root=Path("."), identity=lambda aud: f"jwt-for-{aud}")
167
+
168
+ with mock.patch.multiple(shell, run=run, require=lambda t, h: f"/usr/bin/{t}"):
169
+ env = LambdaTarget(
170
+ role_arn="arn:aws:iam::1:role/deploy", region="us-east-1"
171
+ ).env(ctx)
172
+
173
+ self.assertEqual(env["AWS_ACCESS_KEY_ID"], "AKIA")
174
+ self.assertEqual(env["AWS_SESSION_TOKEN"], "t")
175
+ sts = next(
176
+ c for c in calls if c[1:3] == ["sts", "assume-role-with-web-identity"]
177
+ )
178
+ self.assertIn("jwt-for-sts.amazonaws.com", sts)
179
+ self.assertIn("arn:aws:iam::1:role/deploy", sts)
180
+
181
+ def test_without_an_issuer_or_a_login_it_says_so(self):
182
+ with (
183
+ mock.patch.object(shell, "require", lambda t, h: "/usr/bin/aws"),
184
+ mock.patch.dict("os.environ", {"AP_HOME": "/nonexistent"}, clear=False),
185
+ ):
186
+ with self.assertRaises(DeployError) as caught:
187
+ LambdaTarget(role_arn="arn:aws:iam::1:role/deploy").env(
188
+ Context(repo_root=Path("."))
189
+ )
190
+
191
+ self.assertIn("log in", str(caught.exception))
@@ -1,40 +0,0 @@
1
- # apx-aws-lambda
2
-
3
- AWS Lambda for [Action Platform](https://github.com/actionplatform/action-platform): the `aws/lambda` deploy target (SAM) with its overlay, read-only tools and commands. `apx-` is the prefix every Action Platform extension carries.
4
-
5
- ```bash
6
- action-platform plugin install aws-lambda
7
- action-platform cloud set aws/lambda # overlay files come from this plugin
8
- action-platform deploy --dry-run # preflight: sam validate, credentials
9
- action-platform deploy # sam build + sam deploy --config-env <stage>
10
- action-platform diagnose
11
- ```
12
-
13
- | | |
14
- |---|---|
15
- | Overlay | `template.yaml`, `samconfig.toml`, `lambda_handler.py`, `Makefile`, `requirements/` (IAM), `.github/workflows/deploy.yml` |
16
- | Deploy | `sam build` + `sam deploy --config-env <stage>` (`dev` → `default`, `prod` → `prod`) |
17
- | Rollback | CloudFormation `rollback-stack` — previous stack state |
18
- | Diagnose | stack status and the HTTP API url |
19
- | Destroy | `sam delete` |
20
-
21
- `[deploy]` in platform.toml:
22
-
23
- ```toml
24
- [deploy]
25
- target = "aws/lambda"
26
- region = "us-east-1" # optional; samconfig.toml / AWS_REGION otherwise
27
- ```
28
-
29
- Tools (`action-platform mcp`): `aws_lambda_stacks`, `aws_lambda_functions`. Commands: `action-platform aws-lambda stacks|functions`. Deploying itself goes through the core's `deploy` / `rollback` / `diagnose`, which drive the target.
30
-
31
- Needs: AWS CLI v2 (`aws`), SAM CLI (`sam`), credentials in the environment (`AWS_PROFILE` or keys), `AWS_REGION`. Talks to `*.amazonaws.com` only.
32
-
33
- ## Development
34
-
35
- ```bash
36
- pip install -e ".[dev]"
37
- pytest
38
- ```
39
-
40
- The tests fake `aws` and `sam`; nothing reaches AWS.
File without changes
File without changes