Thanks for contributing to CloudQuery! You are awesome. This document serves as a guide for adding new services and resources to the AWS source plugin.
There are two main steps to adding a new AWS resource:
As a prerequisite, in aws-sdk-go-v2 ensure API calls exist to list/describe the desired resource, and make note of:
- to which aws service the resource belongs
- the schema of the returned object(s)
- Check in client/services.go that the service you need has an interface defined. If it does, you can skip to Step 2. If not, read on to learn how to generate the interface.
- Inside codegen/main.go, add the client for the AWS SDK you need to the
clientsslice. You may need to rungo get github.com/aws/aws-sdk-go-v2/service/<service-name>(e.g.go get github.com/aws/aws-sdk-go-v2/service/dynamodb) to add the dependency first. - Run
make gen-mocks. This takes a few seconds, but it should add the interface for your client to client/services.go and create a mock for it that will be used in unit tests later.
The process to follow for adding a new table is:
-
Add a new directory matching the AWS service name under resources/services (e.g.
resources/services/newservice), if one doesn't exist already -
Create a new file under the new directory with the name of the resource (e.g.
resources/services/newservice/myresource.go) and add a function that returns*schema.Table. The easiest is to copy-paste an existing table as a starting point (Kinesisis a good example). -
Important: Add a call to the new function to the list of tables in tables.go. Otherwise, the new table will not be included in the plugin.
-
Update all the fields, taking special care to ensure that the
transformers.TransformWithStruct()call in theResolverfunction has the correct struct type (e.g.transformers.TransformWithStruct(&types.MyResource{})) -
Implement the resolver function. This should have the signature:
func fetchMyResource(ctx context.Context, meta schema.ClientMeta, parent *schema.Resource, res chan<- interface{}) error { // TODO: implement this }
The easiest is to copy-paste an existing resolver as a starting point. (Again,
Kinesisis a good example.)You may use a type assertion on
metato obtain a reference to your interface functions, e.g.:svc := meta.(*client.Client).Services().MyService
With this in hand, complete the resolver function to fetch all resources. After resources are retrieved, send them to the
reschannel for the SDK to deliver to all destinations. -
Implement a mock test in
myresource_mock_test.go. We will not describe this in detail here; look at a few examples for similar resources to get you started.
We highly recommend looking at other resources similar to yours to get an idea of what needs to be done in each step.
A few important things to note when adding functions that call the AWS API:
- If possible, always use an API call that allows you to fetch many resources at once
- Take pagination into account. Use
Paginators if the AWS service supports it. Ensure you fetch all the resources. - Columns may also have their own resolver functions (not covered in this guide). This may be used for simple transformations or when additional calls can help add further context to the table.
- Many resources require a
Listcall, followed by aDescribecall. Look for examples usingPreResourceResolverto see the canonical way of implementing this. (In short: the table resolver function will callList, while thePreResourceResolver, called once per resource, will callDescribe)
- Keep transformations to a minimum. As far as possible, we aim to deliver an accurate reflection of what the AWS API provides.
- We generally only unroll structs one level deep. Nested structs should be transformed into JSON columns.
- For consistency, make sure the resource has an
ARNstored in a column namedarn. Sometimes this means using the AWS SDK to generate an ARN for the resource. - Make sure the resource has a
tagsJSON column (if possible). Sometimes this requires additional SDK calls. Sometimes the column needs to be renamed fromtag_listtotags(and converted to a map). There are customResolveTagsandResolveTagFieldsresolvers to help with this. It's not always possible, but we try to keep thetagscolumn consistent across AWS resources. - Before submitting a pull request, run
make gen-docsto generate documentation for the table. Include these generated files in the pull request. - If you get stuck or need help, feel free to reach out on Discord. We are a friendly community and would love to help!