Skip to content

Commit

Permalink
feat(secretsManager): add support for versionId in AWS Secrets Manager (
Browse files Browse the repository at this point in the history
external-secrets#436)

* AWS Secrets Manager: Support versionId

Co-authored-by: Markus Maga <[email protected]>
  • Loading branch information
sonots and Flydiverny committed Jul 14, 2020
1 parent 8eb0fbe commit 95827bc
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 3 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ spec:
# Version Stage in Secrets Manager
versionStage: AWSPREVIOUS
property: password_previous
- key: hello-service/credentials
name: password
# Version ID in Secrets Manager
versionId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
property: password_versioned
```

alternatively you can use `dataFrom` and get all the values from hello-service/credentials:
Expand Down
12 changes: 11 additions & 1 deletion lib/backends/kv-backend.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ describe('kv-backend', () => {
it('fetches secret property with version', async () => {
kvBackend._get.onFirstCall().resolves('fakePropertyValue1')
kvBackend._get.onSecondCall().resolves('fakePropertyValue2')
kvBackend._get.onThirdCall().resolves('fakePropertyValue3')

const secretPropertyValues = await kvBackend._fetchDataValues({
data: [{
Expand All @@ -143,6 +144,10 @@ describe('kv-backend', () => {
key: 'fakePropertyKey2',
versionStage: 'AWSPREVIOUS',
name: 'fakePropertyName2'
}, {
key: 'fakePropertyKey3',
versionId: 'ea9ef8d7-ea54-4a3b-b24b-99510e8d7a3d',
name: 'fakePropertyName3'
}],
specOptions: {}
})
Expand All @@ -157,7 +162,12 @@ describe('kv-backend', () => {
keyOptions: { versionStage: 'AWSPREVIOUS' },
specOptions: {}
})
expect(secretPropertyValues).deep.equals([{ fakePropertyName1: 'fakePropertyValue1' }, { fakePropertyName2: 'fakePropertyValue2' }])
expect(kvBackend._get.getCall(2).args[0]).to.deep.equal({
key: 'fakePropertyKey3',
keyOptions: { versionId: 'ea9ef8d7-ea54-4a3b-b24b-99510e8d7a3d' },
specOptions: {}
})
expect(secretPropertyValues).deep.equals([{ fakePropertyName1: 'fakePropertyValue1' }, { fakePropertyName2: 'fakePropertyValue2' }, { fakePropertyName3: 'fakePropertyValue3' }])
})
})

Expand Down
12 changes: 10 additions & 2 deletions lib/backends/secrets-manager-backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ class SecretsManagerBackend extends KVBackend {
* @param {string} key - Key used to store secret property value in Secrets Manager.
* @param {object} keyOptions - Options for this specific key, eg version etc.
* @param {string} keyOptions.versionStage - Version stage
* @param {string} keyOptions.versionId - Version ID
* @param {object} specOptions - Options for this external secret, eg role
* @param {string} specOptions.roleArn - IAM role arn to assume
* @returns {Promise} Promise object representing secret property value.
*/
async _get ({ key, specOptions: { roleArn }, keyOptions: { versionStage = 'AWSCURRENT' } }) {
async _get ({ key, specOptions: { roleArn }, keyOptions: { versionStage = 'AWSCURRENT', versionId = null } }) {
this._logger.info(`fetching secret property ${key} with role: ${roleArn || 'pods role'}`)

let client = this._client
Expand All @@ -41,8 +42,15 @@ class SecretsManagerBackend extends KVBackend {
})
}

let params
if (versionId) {
params = { SecretId: key, VersionId: versionId }
} else {
params = { SecretId: key, VersionStage: versionStage }
}

const data = await client
.getSecretValue({ SecretId: key, VersionStage: versionStage })
.getSecretValue(params)
.promise()

if ('SecretBinary' in data) {
Expand Down
22 changes: 22 additions & 0 deletions lib/backends/secrets-manager-backend.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,27 @@ describe('SecretsManagerBackend', () => {
expect(assumeRoleMock.callCount).equals(0)
expect(secretPropertyValue).equals('fakeSecretPropertyValuePreviousVersion')
})

it('returns secret property value with versionId', async () => {
getSecretValuePromise.promise.resolves({
SecretString: 'fakeSecretPropertyValueVersionId'
})

const secretPropertyValue = await secretsManagerBackend._get({
key: 'fakeSecretKey',
specOptions,
keyOptions: {
versionId: 'ea9ef8d7-ea54-4a3b-b24b-99510e8d7a3d'
}
})

expect(clientMock.getSecretValue.calledWith({
SecretId: 'fakeSecretKey',
VersionId: 'ea9ef8d7-ea54-4a3b-b24b-99510e8d7a3d'
})).to.equal(true)
expect(clientFactoryMock.getCall(0).args).deep.equals([])
expect(assumeRoleMock.callCount).equals(0)
expect(secretPropertyValue).equals('fakeSecretPropertyValueVersionId')
})
})
})
1 change: 1 addition & 0 deletions lib/poller.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const merge = require('lodash.merge')
* @param {string} properties[].property - If the backend secret is an
* object, this is the property name of the value to use.
* @param {string} properties[].versionStage - If the backend supports versioning, eg secretsManager backend
* @param {string} properties[].versionId - If the backend supports versioning, eg secretsManager backend
*/

/** Poller class. */
Expand Down

0 comments on commit 95827bc

Please sign in to comment.