Skip to content

Instantly share code, notes, and snippets.

@mtmorgan
Created January 28, 2021 16:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mtmorgan/b3eec69d74d7e5f2d78a3f0457abc68a to your computer and use it in GitHub Desktop.
Save mtmorgan/b3eec69d74d7e5f2d78a3f0457abc68a to your computer and use it in GitHub Desktop.
AWS ECS REST access
# 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