When working in a multi-account environment with the AWS Cloud Development Kit (CDK), the CDK needs to be able to obtain the appropriate credentials. We are going to show you how to write a credential provider plugin for the CDK.
A CDK App can consist of multiple stacks, each of the stacks can be provided with an AWS Region and Account ID. When deploying, the CDK makes sure that the stacks are deployed to correct account and region.
If you don’t do anything specific, the CDK just checks that the current available AWS credentials correspond to the account and fails if this is not the case. When you provide a credential provider plugin, it will try to obtain the correct credentials from the plugin instead. These plugins are, as of writing, an undocumented experimental feature of the CDK.
The idea is simple, a credential provider plugin translates a 12-digit AWS Account ID into credentials for that account. There is a really simple credential provider source interface that you have to implement.
Obtaining Credentials
In our example credential provider implementation, we want a simple way to obtain the actual credentials. We have found this in the SharedIniFileCredentials
class from the AWS SDK for JavaScript. This class has some quirks and requires an unusual ~/.aws/credentials
file, we specify in this file both static credentials for an IAM User and profiles for IAM Roles that the user is allowed to assume. Normally you would split this in a ~/.aws/credentials
file with the user credentials and a ~/.aws/config
file with the profiles for the other accounts.
The trick here is that we name the profiles with account IDs so that we do not have to look up a profile name for an ID.
# ~/.aws/credentials
[thomas]
aws_access_key_id = AKIAIIIIIIIIIIIIIIII
aws_secret_access_key = AcXXXXXXXXXXXXXXXXXXXXXXXXXDJ+0
[112233445566]
role_arn = arn:aws:iam::112233445566:role/thomas-no-mfa
source_profile = thomas
[223344556677]
role_arn = arn:aws:iam::223344556677:role/thomas-no-mfa
source_profile = thomas
Example Credential Provider Source
The CDK wants to get credentials for a specific AWS Account ID. We implement a credential provider source that uses the SharedIniFileCredentials
class to obtain credentials for profiles in your ~/.aws/credentials
file with the AWS Account ID as the profile name. We discuss the implemented methods below.
// my-credential-provider-source.ts
import * as cdk from "aws-cdk";
import AWS = require("aws-sdk");
export class MyCredentialProviderSource implements cdk.CredentialProviderSource {
name: string;
constructor() {
this.name = "MyCredentialProviderSource";
}
public async isAvailable(): Promise {
return true;
}
public async canProvideCredentials(accountId: string): Promise {
return true;
}
public async getProvider(accountId: string, mode: cdk.Mode): Promise {
return new AWS.SharedIniFileCredentials({ profile: accountId });
}
}
isAvailable
The isAvailable
method, lets the provider tell CDK if it is done loading or not.
We have not yet investigated what would happen when the method returns false
.
canProvideCredentials
The canProvideCredentials
method should test if the provider can provide credentials for the provided accountId
. Although in this example implementation we just return true
, a real-world implementation should parse the ~/.aws/credentials
file and see if a profile for the provided accountId
exists.
getProvider
The getProvider
method must return a Credentials
object with the credentials for the account with the provided accountId
. The mode
argument allows the CDK to specify if credentials with read-only or read-write permissions are required. We have ignored the mode
argument in our example implementation.
Example Plugin
The plugin itself just registers the credential provider source with the CDK.
// my-credential-provider-plugin.ts
import * as cdk from "aws-cdk";
import { MyCredentialProviderSource } from "./my-credential-provider-source";
export class MyCredentialProviderPlugin implements cdk.Plugin {
public readonly version = "1";
public init(host: cdk.PluginHost): void {
host.registerCredentialProviderSource(new MyCredentialProviderSource());
}
}
Packaging the Plugin
Since the credential provider source plugin is addressed by its package name by the CDK, we need to package the plugin.
To emphasize that the module must provide an object instance of the plugin, we show the index.ts
entry point below. The complete example, including the last two essential files package.json
and tsconfig.json
, can be found in our git repository.
Since all code is in TypeScript, we need to build the project: npm run build
// index.ts
import { MyCredentialProviderPlugin } from "./my-credential-provider-plugin";
export = new MyCredentialProviderPlugin();
Using the Plugin
To use the plugin in your CDK project you have to install it. Without publishing your plugin to npm you can install it from your local project:
$ npm install --save-dev ../my-credential-provider
Now you can either specify the plugin as a command-line argument or configure it in the cdk.json
file.
// cdk.json
{
"app": "npx ts-node bin/demo.ts",
"plugin": [
"my-credential-provider"
]
}
Now when you execute npx cdk diff
or npx cdk deploy
the CDK will try to obtain the credentials from the plugin!
Now Go Build!
In the examples above we have shown you how to implement a credential provider plugin for the CDK. You can find the code for the examples in our git repository: github.com/binxio/cdk-plugin-example
Now, go build your own awesome plugin!