Created
January 28, 2021 16:23
-
-
Save mtmorgan/b3eec69d74d7e5f2d78a3f0457abc68a to your computer and use it in GitHub Desktop.
AWS ECS REST access
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# AWS ECS access via REST | |
Resources | |
- [Getting Started with ECS][ECS] | |
- [REST API][API] | |
[ECS]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/getting-started-ecs-ec2.html | |
[API]: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/Welcome.html | |
Prerequisites | |
- AWS account and [access keys][keys] | |
[keys]: https://aws.amazon.com/premiumsupport/knowledge-center/create-access-key/ | |
Goal: list clusters via the [ListClusters][] endpoint | |
[ListClusters]: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ListClusters.html | |
- POST request, body `{}` | |
- Request looks like | |
``` | |
POST / HTTP/1.1 | |
Host: ecs.us-east-1.amazonaws.com | |
Accept-Encoding: identity | |
Content-Length: 2 | |
X-Amz-Target: AmazonEC2ContainerServiceV20141113.ListClusters | |
X-Amz-Date: 20150429T170621Z | |
Content-Type: application/x-amz-json-1.1 | |
Authorization: AUTHPARAMS | |
{} | |
``` | |
- `AUTHPARAMS`: string representing a signed URL | |
[Signed requests][] | |
- Use `aws.signature` package | |
``` | |
host <- paste0(service, ".", region, ".amazonaws.com") | |
datetime <- format(Sys.time(), "%Y%m%dT%H%M%SZ", tz = "UTC") | |
sig <- signature_v4_auth( | |
datetime = datetime, | |
verb = "POST", service = service, action = "/", | |
request_body = request_body, | |
canonical_headers = c( | |
`X-Amz-Date` = datetime, | |
host = host | |
) | |
) | |
``` | |
- `sig` is a named list of values | |
- requires `key`, `secret`, `service = "ecs"`, `region = "us-east-1"` (for example) | |
- `datetime` timestamp; same date used in both signature creation **and** request | |
- `canonical_headers=` elements **must** appear and be identical in both the signature and the HTTP request. | |
[Signed requests]: https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html | |
HTTP request | |
- standard HTTP POST via httr package | |
```{r} | |
SERVICE_ID <- "AmazonEC2ContainerServiceV20141113" | |
response <- POST( | |
url, | |
add_headers( | |
`X-Amz-Date` = datetime, | |
`X-Amz-Target` = paste0(SERVICE_ID, ".", action), | |
`Content-Type` = "application/x-amz-json-1.1", | |
Authorization = sig$SignatureHeader | |
), | |
body = sig$Body | |
) | |
stop_for_status(response) | |
content(response, type = "application/json") | |
``` | |
- `X-Amz-Target` indicates the service and end-point. I don't know where the service identifier is documented, other than in examples in the documentation. | |
- `Content-Type` **must** be `"application/x-amz-json-1.1"`. | |
Implementation | |
- See `?aws.signature::locate_credentials` for managing keys and secrets. | |
```{r} | |
library(httr) | |
library(aws.signature) | |
ecs_POST <- | |
function(action, request_body, region = NULL, key = NULL, secret = NULL) | |
{ | |
credentials <- locate_credentials( | |
key = key, secret = secret, region = region | |
) | |
key <- credentials$key | |
secret <- credentials$secret | |
region <- credentials$region | |
if (!is.character(request_body)) | |
request_body <- jsonlite::toJSON(request_body) | |
stopifnot( | |
is.character(action), length(action) == 1L, | |
is.character(request_body), length(request_body) == 1L, | |
is.character(region), length(region) == 1L, | |
is.character(key), length(key) == 1L, nzchar(key), | |
is.character(secret), length(secret) == 1L, nzchar(secret) | |
) | |
SERVICE_ID <- "AmazonEC2ContainerServiceV20141113" | |
service <- "ecs" | |
host <- paste0(service, ".", region, ".amazonaws.com") | |
url <- paste0("https://", host) | |
datetime <- format(Sys.time(), "%Y%m%dT%H%M%SZ", tz = "UTC") | |
sig <- signature_v4_auth( | |
datetime = datetime, | |
verb = "POST", service = service, action = "/", | |
request_body = request_body, | |
canonical_headers = c( | |
`X-Amz-Date` = datetime, | |
host = host | |
) | |
) | |
response <- POST( | |
url, | |
add_headers( | |
`X-Amz-Date` = datetime, | |
`X-Amz-Target` = paste0(SERVICE_ID, ".", action), | |
`Content-Type` = "application/x-amz-json-1.1", | |
Authorization = sig$SignatureHeader | |
), | |
body = sig$Body | |
) | |
stop_for_status(response) | |
content(response, type = "application/json") | |
} | |
use_credentials() | |
body <- setNames(list(), character()) # jsonlite::toJSON() -> '{}' | |
ecs_POST("ListClusters", body) | |
``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment