diff --git a/.gitleaks.toml b/.gitleaks.toml
new file mode 100644
index 00000000..48362bae
--- /dev/null
+++ b/.gitleaks.toml
@@ -0,0 +1,40 @@
+[extend]
+useDefault = true
+
+[[rules]]
+id = "generic-api-key"
+# all the other attributes from the default rule are inherited
+
+ [[rules.allowlists]]
+ regexTarget = "line"
+ regexes = [
+ '''objectKey''',
+ '''S3Key''',
+ '''SopsAgeKey''',
+ '''s3Key''',
+ ]
+
+[[rules]]
+id = "private-key"
+
+ [[rules.allowlists]]
+ regexTarget = "line"
+ regexes = [
+ '''(.*)OAdqlMznWINBDoyR\+PESgQJlUptwnh(.*)''',
+ ]
+
+[allowlist]
+description = "global allow list"
+paths = [
+ '''\.gitleaks\.toml''',
+ '''lambda/events/(.*?)json''',
+ '''lambda/__snapshots__/(.*?)snap''',
+ '''test-secrets/(.*?)(json|yaml|yml|env|binary)''',
+ '''test/(.*)\.integ\.snapshot/(.*?)json'''
+]
+
+regexTarget = "match"
+regexes = [
+ '''AGE-SECRET-KEY-1EFUWJ0G2XJTJFWTAM2DGMA4VCK3R05W58FSMHZP3MZQ0ZTAQEAFQC6T7T3''',
+]
+
diff --git a/API.md b/API.md
index 4658531e..a986522a 100644
--- a/API.md
+++ b/API.md
@@ -1717,12 +1717,11 @@ const multiStringParameterProps: MultiStringParameterProps = { ... }
| **Name** | **Type** | **Description** |
| --- | --- | --- |
+| autoGenerateIamPermissions
| boolean
| Should this construct automatically create IAM permissions? |
| convertToJSON
| boolean
| Should the encrypted sops value should be converted to JSON? |
-| creationType
| CreationType
| *No description.* |
| flatten
| boolean
| Should the structure be flattened? |
| flattenSeparator
| string
| If the structure should be flattened use the provided separator between keys. |
| parameterKeyPrefix
| string
| Add this prefix to parameter names. |
-| resourceType
| ResourceType
| *No description.* |
| sopsAgeKey
| aws-cdk-lib.SecretValue
| The age key that should be used for encryption. |
| sopsFileFormat
| string
| The format of the sops file. |
| sopsFilePath
| string
| The filepath to the sops file. |
@@ -1746,28 +1745,31 @@ const multiStringParameterProps: MultiStringParameterProps = { ... }
---
-##### `convertToJSON`Optional
+##### `autoGenerateIamPermissions`Optional
```typescript
-public readonly convertToJSON: boolean;
+public readonly autoGenerateIamPermissions: boolean;
```
- *Type:* boolean
- *Default:* true
-Should the encrypted sops value should be converted to JSON?
-
-Only JSON can be handled by cloud formations dynamic references.
+Should this construct automatically create IAM permissions?
---
-##### `creationType`Optional
+##### `convertToJSON`Optional
```typescript
-public readonly creationType: CreationType;
+public readonly convertToJSON: boolean;
```
-- *Type:* CreationType
+- *Type:* boolean
+- *Default:* true
+
+Should the encrypted sops value should be converted to JSON?
+
+Only JSON can be handled by cloud formations dynamic references.
---
@@ -1813,16 +1815,6 @@ Add this prefix to parameter names.
---
-##### `resourceType`Optional
-
-```typescript
-public readonly resourceType: ResourceType;
-```
-
-- *Type:* ResourceType
-
----
-
##### `sopsAgeKey`Optional
```typescript
@@ -2117,12 +2109,11 @@ const sopsSecretProps: SopsSecretProps = { ... }
| secretObjectValue
| {[ key: string ]: aws-cdk-lib.SecretValue}
| Initial value for a JSON secret. |
| secretStringBeta1
| aws-cdk-lib.aws_secretsmanager.SecretStringValueBeta1
| Initial value for the secret. |
| secretStringValue
| aws-cdk-lib.SecretValue
| Initial value for the secret. |
+| autoGenerateIamPermissions
| boolean
| Should this construct automatically create IAM permissions? |
| convertToJSON
| boolean
| Should the encrypted sops value should be converted to JSON? |
-| creationType
| CreationType
| *No description.* |
| flatten
| boolean
| Should the structure be flattened? |
| flattenSeparator
| string
| If the structure should be flattened use the provided separator between keys. |
| parameterKeyPrefix
| string
| Add this prefix to parameter names. |
-| resourceType
| ResourceType
| *No description.* |
| sopsAgeKey
| aws-cdk-lib.SecretValue
| The age key that should be used for encryption. |
| sopsFileFormat
| string
| The format of the sops file. |
| sopsFilePath
| string
| The filepath to the sops file. |
@@ -2308,28 +2299,31 @@ Only one of `secretStringBeta1`, `secretStringValue`, 'secretObjectValue', and `
---
-##### `convertToJSON`Optional
+##### `autoGenerateIamPermissions`Optional
```typescript
-public readonly convertToJSON: boolean;
+public readonly autoGenerateIamPermissions: boolean;
```
- *Type:* boolean
- *Default:* true
-Should the encrypted sops value should be converted to JSON?
-
-Only JSON can be handled by cloud formations dynamic references.
+Should this construct automatically create IAM permissions?
---
-##### `creationType`Optional
+##### `convertToJSON`Optional
```typescript
-public readonly creationType: CreationType;
+public readonly convertToJSON: boolean;
```
-- *Type:* CreationType
+- *Type:* boolean
+- *Default:* true
+
+Should the encrypted sops value should be converted to JSON?
+
+Only JSON can be handled by cloud formations dynamic references.
---
@@ -2375,16 +2369,6 @@ Add this prefix to parameter names.
---
-##### `resourceType`Optional
-
-```typescript
-public readonly resourceType: ResourceType;
-```
-
-- *Type:* ResourceType
-
----
-
##### `sopsAgeKey`Optional
```typescript
@@ -2522,12 +2506,11 @@ const sopsStringParameterProps: SopsStringParameterProps = { ... }
| **Name** | **Type** | **Description** |
| --- | --- | --- |
+| autoGenerateIamPermissions
| boolean
| Should this construct automatically create IAM permissions? |
| convertToJSON
| boolean
| Should the encrypted sops value should be converted to JSON? |
-| creationType
| CreationType
| *No description.* |
| flatten
| boolean
| Should the structure be flattened? |
| flattenSeparator
| string
| If the structure should be flattened use the provided separator between keys. |
| parameterKeyPrefix
| string
| Add this prefix to parameter names. |
-| resourceType
| ResourceType
| *No description.* |
| sopsAgeKey
| aws-cdk-lib.SecretValue
| The age key that should be used for encryption. |
| sopsFileFormat
| string
| The format of the sops file. |
| sopsFilePath
| string
| The filepath to the sops file. |
@@ -2549,28 +2532,31 @@ const sopsStringParameterProps: SopsStringParameterProps = { ... }
---
-##### `convertToJSON`Optional
+##### `autoGenerateIamPermissions`Optional
```typescript
-public readonly convertToJSON: boolean;
+public readonly autoGenerateIamPermissions: boolean;
```
- *Type:* boolean
- *Default:* true
-Should the encrypted sops value should be converted to JSON?
-
-Only JSON can be handled by cloud formations dynamic references.
+Should this construct automatically create IAM permissions?
---
-##### `creationType`Optional
+##### `convertToJSON`Optional
```typescript
-public readonly creationType: CreationType;
+public readonly convertToJSON: boolean;
```
-- *Type:* CreationType
+- *Type:* boolean
+- *Default:* true
+
+Should the encrypted sops value should be converted to JSON?
+
+Only JSON can be handled by cloud formations dynamic references.
---
@@ -2616,16 +2602,6 @@ Add this prefix to parameter names.
---
-##### `resourceType`Optional
-
-```typescript
-public readonly resourceType: ResourceType;
-```
-
-- *Type:* ResourceType
-
----
-
##### `sopsAgeKey`Optional
```typescript
@@ -2891,12 +2867,11 @@ const sopsSyncOptions: SopsSyncOptions = { ... }
| **Name** | **Type** | **Description** |
| --- | --- | --- |
+| autoGenerateIamPermissions
| boolean
| Should this construct automatically create IAM permissions? |
| convertToJSON
| boolean
| Should the encrypted sops value should be converted to JSON? |
-| creationType
| CreationType
| *No description.* |
| flatten
| boolean
| Should the structure be flattened? |
| flattenSeparator
| string
| If the structure should be flattened use the provided separator between keys. |
| parameterKeyPrefix
| string
| Add this prefix to parameter names. |
-| resourceType
| ResourceType
| *No description.* |
| sopsAgeKey
| aws-cdk-lib.SecretValue
| The age key that should be used for encryption. |
| sopsFileFormat
| string
| The format of the sops file. |
| sopsFilePath
| string
| The filepath to the sops file. |
@@ -2909,28 +2884,31 @@ const sopsSyncOptions: SopsSyncOptions = { ... }
---
-##### `convertToJSON`Optional
+##### `autoGenerateIamPermissions`Optional
```typescript
-public readonly convertToJSON: boolean;
+public readonly autoGenerateIamPermissions: boolean;
```
- *Type:* boolean
- *Default:* true
-Should the encrypted sops value should be converted to JSON?
-
-Only JSON can be handled by cloud formations dynamic references.
+Should this construct automatically create IAM permissions?
---
-##### `creationType`Optional
+##### `convertToJSON`Optional
```typescript
-public readonly creationType: CreationType;
+public readonly convertToJSON: boolean;
```
-- *Type:* CreationType
+- *Type:* boolean
+- *Default:* true
+
+Should the encrypted sops value should be converted to JSON?
+
+Only JSON can be handled by cloud formations dynamic references.
---
@@ -2976,16 +2954,6 @@ Add this prefix to parameter names.
---
-##### `resourceType`Optional
-
-```typescript
-public readonly resourceType: ResourceType;
-```
-
-- *Type:* ResourceType
-
----
-
##### `sopsAgeKey`Optional
```typescript
@@ -3109,7 +3077,7 @@ How should the secret be passed to the CustomResource?
### SopsSyncProps
-The configuration options extended by the target Secret.
+The configuration options extended by the target Secret / Parameter.
#### Initializer
@@ -3123,12 +3091,11 @@ const sopsSyncProps: SopsSyncProps = { ... }
| **Name** | **Type** | **Description** |
| --- | --- | --- |
+| autoGenerateIamPermissions
| boolean
| Should this construct automatically create IAM permissions? |
| convertToJSON
| boolean
| Should the encrypted sops value should be converted to JSON? |
-| creationType
| CreationType
| *No description.* |
| flatten
| boolean
| Should the structure be flattened? |
| flattenSeparator
| string
| If the structure should be flattened use the provided separator between keys. |
| parameterKeyPrefix
| string
| Add this prefix to parameter names. |
-| resourceType
| ResourceType
| *No description.* |
| sopsAgeKey
| aws-cdk-lib.SecretValue
| The age key that should be used for encryption. |
| sopsFileFormat
| string
| The format of the sops file. |
| sopsFilePath
| string
| The filepath to the sops file. |
@@ -3139,34 +3106,37 @@ const sopsSyncProps: SopsSyncProps = { ... }
| stringifyValues
| boolean
| Shall all values be flattened? |
| uploadType
| UploadType
| How should the secret be passed to the CustomResource? |
| encryptionKey
| aws-cdk-lib.aws_kms.IKey
| The encryption key used for encrypting the ssm parameter if `parameterName` is set. |
-| parameterName
| string
| The parameter name. |
-| parameterNames
| string[]
| The parameter name. |
+| parameterNames
| string[]
| The parameter names. |
+| resourceType
| ResourceType
| Will this Sync deploy a Secret or Parameter(s). |
| secret
| aws-cdk-lib.aws_secretsmanager.ISecret
| The secret that will be populated with the encrypted sops file content. |
---
-##### `convertToJSON`Optional
+##### `autoGenerateIamPermissions`Optional
```typescript
-public readonly convertToJSON: boolean;
+public readonly autoGenerateIamPermissions: boolean;
```
- *Type:* boolean
- *Default:* true
-Should the encrypted sops value should be converted to JSON?
-
-Only JSON can be handled by cloud formations dynamic references.
+Should this construct automatically create IAM permissions?
---
-##### `creationType`Optional
+##### `convertToJSON`Optional
```typescript
-public readonly creationType: CreationType;
+public readonly convertToJSON: boolean;
```
-- *Type:* CreationType
+- *Type:* boolean
+- *Default:* true
+
+Should the encrypted sops value should be converted to JSON?
+
+Only JSON can be handled by cloud formations dynamic references.
---
@@ -3212,16 +3182,6 @@ Add this prefix to parameter names.
---
-##### `resourceType`Optional
-
-```typescript
-public readonly resourceType: ResourceType;
-```
-
-- *Type:* ResourceType
-
----
-
##### `sopsAgeKey`Optional
```typescript
@@ -3355,31 +3315,29 @@ The encryption key used for encrypting the ssm parameter if `parameterName` is s
---
-##### `parameterName`Optional
+##### `parameterNames`Optional
```typescript
-public readonly parameterName: string;
+public readonly parameterNames: string[];
```
-- *Type:* string
+- *Type:* string[]
-The parameter name.
+The parameter names.
-If set this creates an encrypted SSM Parameter instead of a secret.
+If set this creates encrypted SSM Parameters instead of a secret.
---
-##### `parameterNames`Optional
+##### `resourceType`Optional
```typescript
-public readonly parameterNames: string[];
+public readonly resourceType: ResourceType;
```
-- *Type:* string[]
-
-The parameter name.
+- *Type:* ResourceType
-If set this creates an encrypted SSM Parameter instead of a secret.
+Will this Sync deploy a Secret or Parameter(s).
---
@@ -3411,12 +3369,28 @@ const sopsSyncProviderProps: SopsSyncProviderProps = { ... }
| **Name** | **Type** | **Description** |
| --- | --- | --- |
+| role
| aws-cdk-lib.aws_iam.IRole
| The role that should be used for the custom resource provider. |
| securityGroups
| aws-cdk-lib.aws_ec2.ISecurityGroup[]
| Only if `vpc` is supplied: The list of security groups to associate with the Lambda's network interfaces. |
| vpc
| aws-cdk-lib.aws_ec2.IVpc
| VPC network to place Lambda network interfaces. |
| vpcSubnets
| aws-cdk-lib.aws_ec2.SubnetSelection
| Where to place the network interfaces within the VPC. |
---
+##### `role`Optional
+
+```typescript
+public readonly role: IRole;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IRole
+- *Default:* a new role will be created
+
+The role that should be used for the custom resource provider.
+
+If you don't specify any, a new role will be created with all required permissions
+
+---
+
##### `securityGroups`Optional
```typescript
@@ -3460,31 +3434,6 @@ Where to place the network interfaces within the VPC.
## Enums
-### CreationType
-
-#### Members
-
-| **Name** | **Description** |
-| --- | --- |
-| SINGLE
| Create or update a single secret/parameter. |
-| MULTI
| Create or update a multiple secrets/parameters by flattening the SOPS file. |
-
----
-
-##### `SINGLE`
-
-Create or update a single secret/parameter.
-
----
-
-
-##### `MULTI`
-
-Create or update a multiple secrets/parameters by flattening the SOPS file.
-
----
-
-
### ResourceType
#### Members
@@ -3493,6 +3442,7 @@ Create or update a multiple secrets/parameters by flattening the SOPS file.
| --- | --- |
| SECRET
| *No description.* |
| PARAMETER
| *No description.* |
+| PARAMETER_MULTI
| *No description.* |
---
@@ -3506,6 +3456,11 @@ Create or update a multiple secrets/parameters by flattening the SOPS file.
---
+##### `PARAMETER_MULTI`
+
+---
+
+
### UploadType
#### Members
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ace1023..44316f21 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,4 +2,26 @@
Thanks for your interest in our project. Contributions are welcome. Feel free to [open an issue](issues) with questions or reporting ideas and bugs, or [open pull requests](pulls) to contribute code.
-We are committed to fostering a welcoming, respectful, and harassment-free environment. Be kind!
\ No newline at end of file
+We are committed to fostering a welcoming, respectful, and harassment-free environment. Be kind!
+
+## How to buidl/deploy local
+
+Install all necessary tools with `yarn install` and others manually like `go`
+
+Build the go Lambda code:
+```
+./scripts/build.sh
+```
+Build the package (for CDK development only the first `js` build has to complete):
+```
+yarn projen build
+```
+Link the package:
+```
+yarn link
+```
+Switch to the path/project where you would like to use cdk-sops-secrets. \
+Link the package to your local build source:
+```
+yarn link "cdk-sops-secrets"
+```
\ No newline at end of file
diff --git a/lambda/__snapshots__/handler_parameter_raw_test.snap b/lambda/__snapshots__/handler_parameter_raw_test.snap
index cf7705da..46007613 100755
--- a/lambda/__snapshots__/handler_parameter_raw_test.snap
+++ b/lambda/__snapshots__/handler_parameter_raw_test.snap
@@ -3,7 +3,8 @@
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/binary/sopsfile.enc-age.binary"
+ Key: "../test-secrets/binary/sopsfile.enc-age.binary",
+ ObjectAttributes: ["ETag"]
}
---
diff --git a/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap b/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap
index b33091f7..8826756e 100755
--- a/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap
+++ b/lambda/__snapshots__/handler_parameter_yaml_multi_test.snap
@@ -3,7 +3,8 @@
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
@@ -65,7 +66,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile-complex-parameters.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
diff --git a/lambda/__snapshots__/handler_secret_env_test.snap b/lambda/__snapshots__/handler_secret_env_test.snap
index 20b065d1..0488553e 100755
--- a/lambda/__snapshots__/handler_secret_env_test.snap
+++ b/lambda/__snapshots__/handler_secret_env_test.snap
@@ -3,7 +3,8 @@
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/dotenv/encrypted-best-secret.env"
+ Key: "../test-secrets/dotenv/encrypted-best-secret.env",
+ ObjectAttributes: ["ETag"]
}
---
@@ -46,7 +47,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/dotenv/encrypted-best-secret.env"
+ Key: "../test-secrets/dotenv/encrypted-best-secret.env",
+ ObjectAttributes: ["ETag"]
}
---
diff --git a/lambda/__snapshots__/handler_secret_json_test.snap b/lambda/__snapshots__/handler_secret_json_test.snap
index bbe043fc..83ca106c 100755
--- a/lambda/__snapshots__/handler_secret_json_test.snap
+++ b/lambda/__snapshots__/handler_secret_json_test.snap
@@ -3,7 +3,8 @@
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/json/sopsfile.enc-age.json"
+ Key: "../test-secrets/json/sopsfile.enc-age.json",
+ ObjectAttributes: ["ETag"]
}
---
@@ -46,7 +47,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/json/sopsfile-complex.enc-age.json"
+ Key: "../test-secrets/json/sopsfile-complex.enc-age.json",
+ ObjectAttributes: ["ETag"]
}
---
@@ -89,7 +91,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/json/sopsfile-complex.enc-age.json"
+ Key: "../test-secrets/json/sopsfile-complex.enc-age.json",
+ ObjectAttributes: ["ETag"]
}
---
@@ -132,7 +135,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/json/sopsfile-complex.enc-age.json"
+ Key: "../test-secrets/json/sopsfile-complex.enc-age.json",
+ ObjectAttributes: ["ETag"]
}
---
diff --git a/lambda/__snapshots__/handler_secret_raw_test.snap b/lambda/__snapshots__/handler_secret_raw_test.snap
index 78323e57..674c2da6 100755
--- a/lambda/__snapshots__/handler_secret_raw_test.snap
+++ b/lambda/__snapshots__/handler_secret_raw_test.snap
@@ -3,7 +3,8 @@
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/binary/sopsfile.enc-age.binary"
+ Key: "../test-secrets/binary/sopsfile.enc-age.binary",
+ ObjectAttributes: ["ETag"]
}
---
diff --git a/lambda/__snapshots__/handler_secret_yaml_test.snap b/lambda/__snapshots__/handler_secret_yaml_test.snap
index 72c49a19..9e19453e 100755
--- a/lambda/__snapshots__/handler_secret_yaml_test.snap
+++ b/lambda/__snapshots__/handler_secret_yaml_test.snap
@@ -3,7 +3,8 @@
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
@@ -46,7 +47,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
@@ -89,7 +91,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
@@ -132,7 +135,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
@@ -175,7 +179,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
@@ -218,7 +223,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
@@ -342,7 +348,8 @@ nil
>>>S3ApiMockClient.GetObjectAttributes.Input
{
Bucket: "..",
- Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml"
+ Key: "../test-secrets/yaml/sopsfile-complex.enc-age.yaml",
+ ObjectAttributes: ["ETag"]
}
---
diff --git a/lambda/events/event_create_s3_parameter_raw_simple.json b/lambda/events/event_create_s3_parameter_raw_simple.json
index e3a94b67..0309d8c2 100644
--- a/lambda/events/event_create_s3_parameter_raw_simple.json
+++ b/lambda/events/event_create_s3_parameter_raw_simple.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "PARAMETER",
- "CreationType": "SINGLE",
"ParameterName": "arn:aws:ssm:eu-central-1:123456789012:parameter/testsecret",
"SopsS3File": {
"Bucket": "..",
diff --git a/lambda/events/event_create_s3_parameter_yaml_complex.json b/lambda/events/event_create_s3_parameter_yaml_complex.json
index 620541ba..b84319a1 100644
--- a/lambda/events/event_create_s3_parameter_yaml_complex.json
+++ b/lambda/events/event_create_s3_parameter_yaml_complex.json
@@ -2,8 +2,7 @@
"RequestType": "Create",
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
- "ResourceType": "PARAMETER",
- "CreationType": "MULTI",
+ "ResourceType": "PARAMETER_MULTI",
"ParameterName": "arn:aws:ssm:eu-central-1:123456789012:parameter/testsecret",
"SopsS3File": {
"Bucket": "..",
diff --git a/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json b/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json
index 1e8723e7..893a909e 100644
--- a/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json
+++ b/lambda/events/event_create_s3_parameter_yaml_complex_custom_keys.json
@@ -2,8 +2,7 @@
"RequestType": "Create",
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
- "ResourceType": "PARAMETER",
- "CreationType": "MULTI",
+ "ResourceType": "PARAMETER_MULTI",
"ParameterName": "arn:aws:ssm:eu-central-1:123456789012:parameter/testsecret",
"SopsS3File": {
"Bucket": "..",
diff --git a/lambda/events/event_create_s3_secret_env_as_json_simple.json b/lambda/events/event_create_s3_secret_env_as_json_simple.json
index ba1dc2a5..bc276b4c 100644
--- a/lambda/events/event_create_s3_secret_env_as_json_simple.json
+++ b/lambda/events/event_create_s3_secret_env_as_json_simple.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_env_simple.json b/lambda/events/event_create_s3_secret_env_simple.json
index f39d3d8c..9b1baf91 100644
--- a/lambda/events/event_create_s3_secret_env_simple.json
+++ b/lambda/events/event_create_s3_secret_env_simple.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_json_complex.json b/lambda/events/event_create_s3_secret_json_complex.json
index 74f08b32..a94df6b3 100644
--- a/lambda/events/event_create_s3_secret_json_complex.json
+++ b/lambda/events/event_create_s3_secret_json_complex.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_json_complex_flat.json b/lambda/events/event_create_s3_secret_json_complex_flat.json
index d579f7ae..5a7286bc 100644
--- a/lambda/events/event_create_s3_secret_json_complex_flat.json
+++ b/lambda/events/event_create_s3_secret_json_complex_flat.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_json_complex_stringify.json b/lambda/events/event_create_s3_secret_json_complex_stringify.json
index 533eb648..b08f818d 100644
--- a/lambda/events/event_create_s3_secret_json_complex_stringify.json
+++ b/lambda/events/event_create_s3_secret_json_complex_stringify.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_json_simple.json b/lambda/events/event_create_s3_secret_json_simple.json
index 4f423508..5d657957 100644
--- a/lambda/events/event_create_s3_secret_json_simple.json
+++ b/lambda/events/event_create_s3_secret_json_simple.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_raw_simple.json b/lambda/events/event_create_s3_secret_raw_simple.json
index 8c3a1b85..8f85ba91 100644
--- a/lambda/events/event_create_s3_secret_raw_simple.json
+++ b/lambda/events/event_create_s3_secret_raw_simple.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_yaml_as_json_complex.json b/lambda/events/event_create_s3_secret_yaml_as_json_complex.json
index 9e44b39c..48801dad 100644
--- a/lambda/events/event_create_s3_secret_yaml_as_json_complex.json
+++ b/lambda/events/event_create_s3_secret_yaml_as_json_complex.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json b/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json
index 9e88f100..995f00ed 100644
--- a/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json
+++ b/lambda/events/event_create_s3_secret_yaml_as_json_complex_flat.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_yaml_as_json_simple.json b/lambda/events/event_create_s3_secret_yaml_as_json_simple.json
index d43d869f..02420ea7 100644
--- a/lambda/events/event_create_s3_secret_yaml_as_json_simple.json
+++ b/lambda/events/event_create_s3_secret_yaml_as_json_simple.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_yaml_complex.json b/lambda/events/event_create_s3_secret_yaml_complex.json
index 4ee77841..a2e0403d 100644
--- a/lambda/events/event_create_s3_secret_yaml_complex.json
+++ b/lambda/events/event_create_s3_secret_yaml_complex.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_yaml_complex_flat.json b/lambda/events/event_create_s3_secret_yaml_complex_flat.json
index 7e4ad675..d7d8cc78 100644
--- a/lambda/events/event_create_s3_secret_yaml_complex_flat.json
+++ b/lambda/events/event_create_s3_secret_yaml_complex_flat.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/events/event_create_s3_secret_yaml_simple.json b/lambda/events/event_create_s3_secret_yaml_simple.json
index 6d82161d..8fecbe62 100644
--- a/lambda/events/event_create_s3_secret_yaml_simple.json
+++ b/lambda/events/event_create_s3_secret_yaml_simple.json
@@ -3,7 +3,6 @@
"LogicalResourceId": "LogicalResourceId",
"ResourceProperties": {
"ResourceType": "SECRET",
- "CreationType": "SINGLE",
"FlattenSeparator": ".",
"SecretARN": "arn:aws:secretsmanager:eu-central-1:123456789012:secret:testsecret",
"SopsS3File": {
diff --git a/lambda/main.go b/lambda/main.go
index 2b6f3f8e..5ab7fd30 100644
--- a/lambda/main.go
+++ b/lambda/main.go
@@ -4,7 +4,6 @@ import (
"context"
"encoding/base64"
"encoding/json"
- "errors"
"fmt"
"log"
"reflect"
@@ -51,7 +50,6 @@ type SopsSyncResourcePropertys struct {
FlattenSeparator string `json:"FlattenSeparator,omitempty"`
ParameterKeyPrefix string `json:"ParameterKeyPrefix,omitempty"`
StringifyValues string `json:"StringifyValues,omitempty"`
- CreationType string `json:"CreationType,omitempty"`
ResourceType string `json:"ResourceType,omitempty"`
}
@@ -70,7 +68,7 @@ func (a AWS) getS3FileContent(file SopsS3File) (data []byte, err error) {
Key: &file.Key,
})
if err != nil {
- return nil, errors.New(fmt.Sprintf("S3 download error:\n%v\n", err))
+ return nil, fmt.Errorf("S3 download error:\n%v", err)
}
log.Printf("Downloaded %d bytes", resp)
return buf.Bytes(), nil
@@ -80,7 +78,7 @@ func decryptSopsFileContent(content []byte, format string) (data []byte, err err
log.Printf("Decrypting content with format %s\n", format)
resp, err := decrypt.Data(content, format)
if err != nil {
- return nil, errors.New(fmt.Sprintf("Decryption error:\n%v\n", err))
+ return nil, fmt.Errorf("decryption error:\n%v", err)
}
log.Println("Decrypted")
return resp, nil
@@ -95,7 +93,7 @@ func (a AWS) updateSecret(sopsHash string, secretArn string, secretContent []byt
}
secretResp, secretErr := a.secretsmanager.PutSecretValue(input)
if secretErr != nil {
- return nil, errors.New(fmt.Sprintf("Failed to store secret value:\nsecretArn: %s\nClientRequestToken: %s\n%v\n", secretArn, sopsHash, secretErr))
+ return nil, fmt.Errorf("failed to store secret value:\nsecretArn: %s\nClientRequestToken: %s\n%v", secretArn, sopsHash, secretErr)
}
arn := generatePhysicalResourceId(*secretResp.ARN)
secretResp.ARN = &arn
@@ -114,7 +112,7 @@ func (a AWS) updateSSMParameter(parameterName string, parameterContent []byte, k
}
paramResp, paramErr := a.ssm.PutParameter(input)
if paramErr != nil {
- return nil, errors.New(fmt.Sprintf("Failed to store parameter value:\nparameterName: %s\n%v\n", parameterName, paramErr))
+ return nil, fmt.Errorf("failed to store parameter value:\nparameterName: %s\n%v", parameterName, paramErr)
}
log.Printf("Successfully stored parameter:\n%v\n", paramResp)
return paramResp, nil
@@ -156,13 +154,19 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
attr, err := a.s3Api.GetObjectAttributes(&s3.GetObjectAttributesInput{
Bucket: &sopsFile.Bucket,
Key: &sopsFile.Key,
+ ObjectAttributes: []*string{
+ aws.String("ETag"),
+ },
})
+ if err != nil {
+ return tempArn, nil, fmt.Errorf("error while getting S3 object attributes:\n%v", err)
+ }
encryptedContent, err = a.getS3FileContent(sopsFile)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("error while getting S3 file content:\n%v", err)
}
if attr.ETag == nil {
- return tempArn, nil, errors.New("No ETag checksum found in S3 object")
+ return tempArn, nil, fmt.Errorf("no ETag checksum found in S3 object:\n%v", err)
}
sopsHash = *attr.ETag
}
@@ -177,15 +181,15 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
}
if encryptedContent == nil {
- return tempArn, nil, errors.New("No encrypted content found! Did you pass SopsS3File or SopsInline?")
+ return tempArn, nil, fmt.Errorf("no encrypted content found! Did you pass SopsS3File or SopsInline:\n%v", err)
}
if sopsHash == "" {
- return tempArn, nil, errors.New("No sopsHash found! Did you pass SopsS3File or SopsInline?")
+ return tempArn, nil, fmt.Errorf("no sopsHash found! Did you pass SopsS3File or SopsInline:\n%v", err)
}
decryptedContent, err := decryptSopsFileContent(encryptedContent, resourceProperties.Format)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("error while decrypting file content\n%v", err)
}
var decryptedInterface interface{}
switch resourceProperties.Format {
@@ -193,14 +197,14 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
{
err := json.Unmarshal(decryptedContent, &decryptedInterface)
if err != nil {
- return tempArn, nil, fmt.Errorf("Failed to parse json content: %v", err)
+ return tempArn, nil, fmt.Errorf("failed to parse json content:\n%v", err)
}
}
case "yaml":
{
err := yaml.Unmarshal(decryptedContent, &decryptedInterface)
if err != nil {
- return tempArn, nil, fmt.Errorf("Failed to parse yaml content: %v", err)
+ return tempArn, nil, fmt.Errorf("failed to parse yaml content:\n%v", err)
}
}
case "dotenv":
@@ -228,14 +232,14 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
resourceProperties.ConvertToJSON = "false"
}
default:
- return "", nil, errors.New(fmt.Sprintf("Format %s not supported", resourceProperties.Format))
+ return "", nil, fmt.Errorf("format %s not supported", resourceProperties.Format)
}
if resourceProperties.Flatten == "" {
resourceProperties.Flatten = "true"
}
resourcePropertiesFlatten, err := strconv.ParseBool(resourceProperties.Flatten)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to parse bool:\n%v", err)
}
var finalInterface interface{}
@@ -244,7 +248,7 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
flattenedInterface := make(map[string]interface{})
err := flatten("", decryptedInterface, flattenedInterface, resourceProperties.FlattenSeparator)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to flatten:\n%v", err)
}
finalInterface = flattenedInterface
} else {
@@ -256,12 +260,12 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
}
resourcePropertiesStringifyValues, err := strconv.ParseBool(resourceProperties.StringifyValues)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to parse bool:\n%v", err)
}
if resourcePropertiesStringifyValues {
finalInterface, _, err = stringifyValues(finalInterface)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to stringify values:\n%v", err)
}
}
@@ -270,24 +274,24 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
}
resourcePropertieConvertToJSON, err := strconv.ParseBool(resourceProperties.ConvertToJSON)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to parse bool:\n%v", err)
}
if resourcePropertieConvertToJSON || resourceProperties.Format == "json" {
decryptedContent, err = toJSON(finalInterface)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to convert to JSON:\n%v", err)
}
} else if resourceProperties.Format == "yaml" {
decryptedContent, err = toYAML(finalInterface)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to convert to YAML:\n%v", err)
}
}
if resourceProperties.ResourceType == "SECRET" {
updateSecretResp, err := a.updateSecret(sopsHash, resourceProperties.SecretARN, decryptedContent)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to update secret:\n%v", err)
}
returnData := make(map[string]interface{})
returnData["ARN"] = *updateSecretResp.ARN
@@ -295,52 +299,49 @@ func (a AWS) syncSopsToSecretsmanager(ctx context.Context, event cfn.Event) (phy
returnData["VersionStages"] = updateSecretResp.VersionStages
returnData["VersionId"] = *updateSecretResp.VersionId
return *updateSecretResp.ARN, returnData, nil
- } else if resourceProperties.ResourceType == "PARAMETER" {
- if resourceProperties.CreationType == "MULTI" && resourcePropertiesFlatten {
- log.Printf("Patching multiple string parameters")
- v := reflect.ValueOf(finalInterface)
- returnData := make(map[string]interface{})
- keys := v.MapKeys()
- keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) }
- sort.Slice(keys, keysOrder)
- for _, key := range keys {
- strKey := resourceProperties.ParameterKeyPrefix + key.String()
- log.Printf("Parameter: " + strKey)
- value := v.MapIndex(key).Interface()
- strValue, ok := value.(string)
- if !ok {
- return tempArn, nil, nil
- }
-
- _, err := a.updateSSMParameter(strKey, []byte(strValue), resourceProperties.EncryptionKey)
- if err != nil {
- return tempArn, nil, err
- }
- // A returnData map for each parameter is not created, because it would limit the number of possible parameters unnecessarily
+ } else if resourceProperties.ResourceType == "PARAMETER_MULTI" {
+ log.Printf("Patching multiple string parameters")
+ v := reflect.ValueOf(finalInterface)
+ returnData := make(map[string]interface{})
+ keys := v.MapKeys()
+ keysOrder := func(i, j int) bool { return keys[i].Interface().(string) < keys[j].Interface().(string) }
+ sort.Slice(keys, keysOrder)
+ for _, key := range keys {
+ strKey := resourceProperties.ParameterKeyPrefix + key.String()
+ log.Printf("Parameter: " + strKey)
+ value := v.MapIndex(key).Interface()
+ strValue, ok := value.(string)
+ if !ok {
+ return tempArn, nil, nil
}
- returnData["Prefix"] = resourceProperties.ParameterKeyPrefix
- returnData["Count"] = len(keys)
- return tempArn, returnData, nil
- } else {
- log.Printf("Patching single string parameter")
- response, err := a.updateSSMParameter(resourceProperties.ParameterName, decryptedContent, resourceProperties.EncryptionKey)
+ _, err := a.updateSSMParameter(strKey, []byte(strValue), resourceProperties.EncryptionKey)
if err != nil {
- return tempArn, nil, err
+ return tempArn, nil, fmt.Errorf("failed to update ssm parameter:\n%v", err)
}
- returnData := make(map[string]interface{})
- returnData["ParameterName"] = resourceProperties.ParameterName
- returnData["Version"] = response.Version
- returnData["Tier"] = response.Tier
- return tempArn, returnData, nil
+ // A returnData map for each parameter is not created, because it would limit the number of possible parameters unnecessarily
}
+ returnData["Prefix"] = resourceProperties.ParameterKeyPrefix
+ returnData["Count"] = len(keys)
+ return tempArn, returnData, nil
+ } else if resourceProperties.ResourceType == "PARAMETER" {
+ log.Printf("Patching single string parameter")
+ response, err := a.updateSSMParameter(resourceProperties.ParameterName, decryptedContent, resourceProperties.EncryptionKey)
+ if err != nil {
+ return tempArn, nil, fmt.Errorf("failed to update ssm parameter:\n%v", err)
+ }
+ returnData := make(map[string]interface{})
+ returnData["ParameterName"] = resourceProperties.ParameterName
+ returnData["Version"] = response.Version
+ returnData["Tier"] = response.Tier
+ return tempArn, returnData, nil
} else {
// Should never happen ...
- return tempArn, nil, errors.New("Neither SecretARN nor ParameterName is provided")
+ return tempArn, nil, fmt.Errorf("neither SecretARN nor ParameterName is provided:\n%v", err)
}
} else if event.RequestType == cfn.RequestDelete {
return "", nil, nil
} else {
- return "", nil, errors.New(fmt.Sprintf("RequestType '%s' not supported", event.RequestType))
+ return "", nil, fmt.Errorf("requestType '%s' not supported", event.RequestType)
}
}
@@ -350,6 +351,7 @@ func handleRequest(ctx context.Context, event cfn.Event) (physicalResourceID str
secretsmanager: secretsmanager.New(awsSession),
ssm: ssm.New(awsSession),
s3Downloader: s3manager.NewDownloader(awsSession),
+ s3Api: s3.New(awsSession),
}
return a.syncSopsToSecretsmanager(ctx, event)
}
diff --git a/scripts/lambda-build.sh b/scripts/lambda-build.sh
index 7f46f771..06fda667 100755
--- a/scripts/lambda-build.sh
+++ b/scripts/lambda-build.sh
@@ -8,8 +8,8 @@ export GOPROXY=https://proxy.golang.org,direct
export CGO_ENABLED=0
go build -trimpath -buildvcs=false -tags lambda.norpc -o bootstrap -ldflags="-s -w -buildid="
ls -la bootstrap
-shasum bootstrap
+sha1sum bootstrap
touch -t 202002020000 bootstrap
chmod 755 bootstrap
ls -la bootstrap
-shasum bootstrap
+sha1sum bootstrap
diff --git a/src/MultiStringParameter.ts b/src/MultiStringParameter.ts
index cc7f886c..a2c98478 100644
--- a/src/MultiStringParameter.ts
+++ b/src/MultiStringParameter.ts
@@ -5,12 +5,7 @@ import { ResourceEnvironment, Stack } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import * as YAML from 'yaml';
import { SopsStringParameterProps } from './SopsStringParameter';
-import {
- CreationType,
- ResourceType,
- SopsSync,
- SopsSyncOptions,
-} from './SopsSync';
+import { ResourceType, SopsSync, SopsSyncOptions } from './SopsSync';
interface JSONObject {
[key: string]: any;
@@ -83,8 +78,7 @@ export class MultiStringParameter extends Construct {
this.sync = new SopsSync(this, 'SopsSync', {
encryptionKey: this.encryptionKey,
- resourceType: ResourceType.PARAMETER,
- creationType: CreationType.MULTI,
+ resourceType: ResourceType.PARAMETER_MULTI,
flatten: true,
flattenSeparator: this.keySeparator,
parameterKeyPrefix: this.keyPrefix,
diff --git a/src/SopsSecret.ts b/src/SopsSecret.ts
index a3c8c121..ba574851 100644
--- a/src/SopsSecret.ts
+++ b/src/SopsSecret.ts
@@ -20,12 +20,7 @@ import {
Stack,
} from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
-import {
- CreationType,
- ResourceType,
- SopsSync,
- SopsSyncOptions,
-} from './SopsSync';
+import { ResourceType, SopsSync, SopsSyncOptions } from './SopsSync';
/**
* The configuration options of the SopsSecret
@@ -63,7 +58,6 @@ export class SopsSecret extends Construct implements ISecret {
this.sync = new SopsSync(this, 'SopsSync', {
secret: this.secret,
resourceType: ResourceType.SECRET,
- creationType: CreationType.SINGLE,
flattenSeparator: '.',
...(props as SopsSyncOptions),
});
diff --git a/src/SopsStringParameter.ts b/src/SopsStringParameter.ts
index cbb3aad2..42321106 100644
--- a/src/SopsStringParameter.ts
+++ b/src/SopsStringParameter.ts
@@ -7,7 +7,7 @@ import {
} from 'aws-cdk-lib/aws-ssm';
import { RemovalPolicy, ResourceEnvironment, Stack } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
-import { SopsSync, SopsSyncOptions } from './SopsSync';
+import { ResourceType, SopsSync, SopsSyncOptions } from './SopsSync';
/**
* The configuration options of the StringParameter
@@ -58,7 +58,8 @@ export class SopsStringParameter extends Construct implements IStringParameter {
this.sync = new SopsSync(this, 'SopsSync', {
encryptionKey: this.parameter.encryptionKey,
- parameterName: this.parameter.parameterName,
+ parameterNames: [this.parameter.parameterName],
+ resourceType: ResourceType.PARAMETER,
...(props as SopsSyncOptions),
});
}
diff --git a/src/SopsSync.ts b/src/SopsSync.ts
index 244d0976..fcffb5aa 100644
--- a/src/SopsSync.ts
+++ b/src/SopsSync.ts
@@ -33,20 +33,10 @@ export enum UploadType {
ASSET = 'ASSET',
}
-export enum CreationType {
- /**
- * Create or update a single secret/parameter
- */
- SINGLE = 'SINGLE',
- /**
- * Create or update a multiple secrets/parameters by flattening the SOPS file
- */
- MULTI = 'MULTI',
-}
-
export enum ResourceType {
SECRET = 'SECRET',
PARAMETER = 'PARAMETER',
+ PARAMETER_MULTI = 'PARAMETER_MULTI',
}
/**
@@ -141,12 +131,16 @@ export interface SopsSyncOptions {
*/
readonly stringifyValues?: boolean;
- readonly creationType?: CreationType;
- readonly resourceType?: ResourceType;
+ /**
+ * Should this construct automatically create IAM permissions?
+ *
+ * @default true
+ */
+ readonly autoGenerateIamPermissions?: boolean;
}
/**
- * The configuration options extended by the target Secret
+ * The configuration options extended by the target Secret / Parameter
*/
export interface SopsSyncProps extends SopsSyncOptions {
/**
@@ -155,12 +149,7 @@ export interface SopsSyncProps extends SopsSyncOptions {
readonly secret?: ISecret;
/**
- * The parameter name. If set this creates an encrypted SSM Parameter instead of a secret.
- */
- readonly parameterName?: string;
-
- /**
- * The parameter name. If set this creates an encrypted SSM Parameter instead of a secret.
+ * The parameter names. If set this creates encrypted SSM Parameters instead of a secret.
*/
readonly parameterNames?: string[];
@@ -168,6 +157,11 @@ export interface SopsSyncProps extends SopsSyncOptions {
* The encryption key used for encrypting the ssm parameter if `parameterName` is set.
*/
readonly encryptionKey?: IKey;
+
+ /**
+ * Will this Sync deploy a Secret or Parameter(s)
+ */
+ readonly resourceType?: ResourceType;
}
/**
@@ -192,6 +186,14 @@ export interface SopsSyncProviderProps {
* @default - A dedicated security group will be created for the lambda function.
*/
readonly securityGroups?: ISecurityGroup[];
+
+ /**
+ * The role that should be used for the custom resource provider.
+ * If you don't specify any, a new role will be created with all required permissions
+ *
+ * @default - a new role will be created
+ */
+ readonly role?: IRole;
}
export class SopsSyncProvider extends SingletonFunction implements IGrantable {
@@ -206,6 +208,7 @@ export class SopsSyncProvider extends SingletonFunction implements IGrantable {
runtime: Runtime.PROVIDED_AL2,
handler: 'bootstrap',
uuid: 'SopsSyncProvider',
+ role: props?.role,
timeout: Duration.seconds(60),
environment: {
SOPS_AGE_KEY: Lazy.string({
@@ -268,8 +271,11 @@ export class SopsSync extends Construct {
let sopsS3File: { Bucket: string; Key: string } | undefined = undefined;
if (
- props.sopsFilePath !== undefined &&
- (props.sopsS3Bucket !== undefined || props.sopsS3Key !== undefined)
+ (props.sopsFilePath == undefined &&
+ (props.sopsS3Bucket == undefined || props.sopsS3Key == undefined)) ||
+ (props.sopsFilePath !== undefined &&
+ props.sopsS3Bucket !== undefined &&
+ props.sopsS3Key !== undefined)
) {
throw new Error(
'You can either specify sopsFilePath or sopsS3Bucket and sopsS3Key!',
@@ -312,11 +318,12 @@ export class SopsSync extends Construct {
if (!fs.existsSync(props.sopsFilePath)) {
throw new Error(`File ${props.sopsFilePath} does not exist!`);
}
+ const sopsFileContent = fs.readFileSync(props.sopsFilePath);
switch (uploadType) {
case UploadType.INLINE: {
sopsInline = {
- Content: fs.readFileSync(props.sopsFilePath).toString('base64'),
+ Content: sopsFileContent.toString('base64'),
// We calculate the hash the same way as it would be done by new Asset(..) - so we can ensure stable version names even if switching from INLINE to ASSET and viceversa.
Hash: FileSystem.fingerprint(props.sopsFilePath),
};
@@ -334,71 +341,29 @@ export class SopsSync extends Construct {
}
}
- if (provider.role !== undefined) {
- if (props.sopsKmsKey !== undefined) {
- props.sopsKmsKey.forEach((key) => key.grantDecrypt(provider.role!));
- }
- const fileContent = fs.readFileSync(props.sopsFilePath);
- // Handle keys
- const regexKey = /arn:aws:kms:[a-z0-9-]+:[\d]+:key\/[a-z0-9-]+/g;
- const resultsKey = fileContent.toString().match(regexKey);
- if (resultsKey !== undefined) {
- resultsKey?.forEach((result, index) =>
- Key.fromKeyArn(this, `SopsKey${index}`, result).grantDecrypt(
- provider.role!,
- ),
- );
- }
- const regexAlias = /arn:aws:kms:[a-z0-9-]+:[\d]+:alias\/[a-z0-9-]+/g;
- const resultsAlias = fileContent.toString().match(regexAlias);
- if (resultsAlias !== undefined) {
- resultsAlias?.forEach((result, index) =>
- Key.fromLookup(this, `SopsAlias${index}`, {
- aliasName: `alias/${result.split('/').slice(1).join('/')}`,
- }).grantDecrypt(provider.role!),
- );
- }
- if (props.secret) {
- props.secret.grantWrite(provider);
- props.secret.encryptionKey?.grantEncryptDecrypt(provider);
- if (props.secret?.encryptionKey !== undefined) {
- props.secret.encryptionKey.grantEncryptDecrypt(provider);
- }
- }
- if (props.parameterName) {
- provider.addToRolePolicy(
- new PolicyStatement({
- actions: ['ssm:PutParameter'],
- resources: [
- `arn:aws:ssm:${Stack.of(this).region}:${
- Stack.of(this).account
- }:parameter${
- props.parameterName.startsWith('/')
- ? props.parameterName
- : `/${props.parameterName}`
- }`,
- ],
- }),
- );
- props.encryptionKey?.grantEncryptDecrypt(provider);
- }
- if (props.parameterNames) {
- this.createReducedParameterPolicy(
- props.parameterNames,
- provider.role,
- );
- props.encryptionKey?.grantEncryptDecrypt(provider);
- }
- if (sopsAsset !== undefined) {
- sopsAsset.bucket.grantRead(provider);
- }
+ if (
+ // Is allways true, but to satisfy TS we check explicitly
+ provider.role !== undefined &&
+ // Check if user has disabled automatic generation
+ props.autoGenerateIamPermissions !== false
+ ) {
+ Permissions.sopsKeys(this, {
+ userDefinedKeys: props.sopsKmsKey,
+ role: provider.role,
+ sopsFileContent: sopsFileContent.toString(),
+ });
+ Permissions.assetBucket(sopsAsset, provider.role);
+ Permissions.encryptionKey(props.encryptionKey, provider.role);
+ Permissions.secret(props.secret, provider.role);
+ Permissions.parameters(this, props.parameterNames, provider.role);
} else {
Annotations.of(this).addWarning(
- `Please ensure proper permissions for the passed lambda function:\n - write Access to the secret\n - encrypt with the sopsKmsKey${
- uploadType === UploadType.ASSET
- ? '\n - download from asset bucket'
- : ''
- }`,
+ [
+ 'Please ensure proper permissions for the passed lambda function:',
+ ' - write Access to the secret/parameters',
+ ' - encrypt with the sopsKmsKey',
+ ' - download from asset bucket',
+ ].join('\n'),
);
}
if (props.sopsAgeKey !== undefined) {
@@ -416,10 +381,6 @@ export class SopsSync extends Construct {
Annotations.of(this).addWarning(
'You have to manually add permissions to the sops provider to (permission to download file, to decrypt sops file)!',
);
- } else {
- throw new Error(
- 'You have to specify both sopsS3Bucket and sopsS3Key or neither!',
- );
}
if (sopsFileFormat === undefined) {
@@ -439,90 +400,169 @@ export class SopsSync extends Construct {
ParameterKeyPrefix: props.parameterKeyPrefix,
Format: sopsFileFormat,
StringifiedValues: this.stringifiedValues,
- ParameterName: props.parameterName,
+ ParameterName:
+ // Dirty Workaround, refactor ...
+ props.parameterNames && props.parameterNames.length == 1
+ ? props.parameterNames[0]
+ : undefined,
EncryptionKey:
props.secret !== undefined ? undefined : props.encryptionKey?.keyId,
ResourceType: props.resourceType
? props.resourceType.toString()
: ResourceType.SECRET.toString(),
- CreationType: props.creationType
- ? props.creationType.toString()
- : CreationType.SINGLE.toString(),
},
});
this.versionId = cr.getAttString('VersionId');
}
+}
- private createReducedParameterPolicy(parameters: string[], role: IRole) {
- // Avoid too large policies
- // The maximum size of a managed policy is 6.144 bytes -> 1 character = 1 byte
- const maxPolicyBytes = 6000; // Keep some bytes as a buffer
- const arnPrefixBytes = 55; // Content for "arn:aws:ssm:ap-southeast-3::parameter/
- let startAtParameter = 0;
- let currentPolicyBytes = 300; // Reserve some byte space for basic stuff inside the policy
- for (let i = 0; i < parameters.length; i += 1) {
- if (
- // Check if the current parameter would fit into the policy
- arnPrefixBytes + parameters[i].length + currentPolicyBytes <
- maxPolicyBytes
- ) {
- // If so increase the byte counter
- currentPolicyBytes =
- arnPrefixBytes + parameters[i].length + currentPolicyBytes;
- } else {
- const parameterNamesChunk = parameters.slice(
- startAtParameter,
- i, //end of slice is not included
- );
- startAtParameter = i;
- currentPolicyBytes = 300;
- // Create the policy for the selected chunk
- const putPolicy = new ManagedPolicy(
- this,
- `SopsSecretParameterProviderManagedPolicyParameterAccess${i}`,
- {
- description:
- 'Policy to grant parameter provider permissions to put parameter',
- },
- );
- putPolicy.addStatements(
- new PolicyStatement({
- actions: ['ssm:PutParameter'],
- resources: parameterNamesChunk.map(
- (param) =>
- `arn:aws:ssm:${Stack.of(this).region}:${
- Stack.of(this).account
- }:parameter${param.startsWith('/') ? param : `/${param}`}`,
- ),
- }),
- );
- role.addManagedPolicy(putPolicy);
+export namespace Permissions {
+ /**
+ * Grants the necessary permissions for encrypt/decrypt on the customer managed encryption key
+ * for the secrets / parameters.
+ */
+ export function encryptionKey(key: IKey | undefined, target: IGrantable) {
+ if (key === undefined) {
+ return;
+ }
+ key.grantEncryptDecrypt(target);
+ }
+
+ export function keysFromSopsContent(ctx: Construct, c: string): IKey[] {
+ const regexKey = /arn:aws:kms:[a-z0-9-]+:[\d]+:key\/[a-z0-9-]+/g;
+ const resultsKey = c.match(regexKey);
+ if (resultsKey !== null) {
+ return resultsKey.map((result, index) =>
+ Key.fromKeyArn(ctx, `SopsKey${index}`, result),
+ );
+ }
+ return [];
+ }
+
+ export function keysFromSopsContentAlias(ctx: Construct, c: string): IKey[] {
+ const regexAlias = /arn:aws:kms:[a-z0-9-]+:[\d]+:alias\/[a-z0-9-]+/g;
+ const resultsAlias = c.match(regexAlias);
+ if (resultsAlias !== null) {
+ return resultsAlias.map((result, index) =>
+ Key.fromLookup(ctx, `SopsAlias${index}`, {
+ aliasName: `alias/${result.split('/').slice(1).join('/')}`,
+ }),
+ );
+ }
+ return [];
+ }
+
+ /**
+ * Grants the necessary permissions to decrypt the given sops file content.
+ * Takes user defined keys, and searches the sops file for keys and aliases.
+ */
+ export function sopsKeys(
+ ctx: Construct,
+ props: {
+ userDefinedKeys?: IKey[];
+ sopsFileContent: string;
+ role: IRole;
+ },
+ ) {
+ (props.userDefinedKeys ?? [])
+ .concat(
+ keysFromSopsContent(ctx, props.sopsFileContent),
+ keysFromSopsContentAlias(ctx, props.sopsFileContent),
+ )
+ .forEach((key) => key.grantDecrypt(props.role));
+ }
+
+ /**
+ * Grants the necessary permissions to write the given secrets.
+ */
+ export function secret(
+ targetSecret: ISecret | undefined,
+ target: IGrantable,
+ ) {
+ if (targetSecret === undefined) {
+ return;
+ }
+ targetSecret.grantWrite(target);
+ }
+
+ function sliceParameters(params: string[]): string[][] {
+ const result: string[][] = [];
+ /**
+ * The maximum size of a managed policy is 6.144 bytes -> 1 character = 1 byte
+ * bout 300 characters are reserved for the policy apart from resource arns
+ * with some buffer, we end with an upper limit of 5750 bytes
+ */
+ const limit = 5750;
+
+ /**
+ * Content for "arn:aws:ssm:ap-southeast-3::parameter/
+ */
+ const prefix = 55;
+
+ let currentSize = 0;
+ let currentChunk: string[] = [];
+ for (const param of params) {
+ const paramLength = param.length + prefix;
+ if (currentSize + paramLength > limit) {
+ result.push(currentChunk);
+ currentChunk = [];
+ currentSize = 0;
}
+ currentChunk.push(param);
+ currentSize += paramLength;
}
- const parameterNamesChunk = parameters.slice(
- startAtParameter,
- parameters.length,
- );
- // Create the policy for the remaning elements
- const putPolicy = new ManagedPolicy(
- this,
- `SopsSecretParameterProviderManagedPolicyParameterAccess${parameters.length}`,
- {
- description:
- 'Policy to grant parameter provider permissions to put parameter',
- },
- );
- putPolicy.addStatements(
- new PolicyStatement({
- actions: ['ssm:PutParameter'],
- resources: parameterNamesChunk.map(
- (param) =>
- `arn:aws:ssm:${Stack.of(this).region}:${
- Stack.of(this).account
- }:parameter${param.startsWith('/') ? param : `/${param}`}`,
- ),
- }),
- );
- role.addManagedPolicy(putPolicy);
+
+ if (currentChunk.length > 0) {
+ result.push(currentChunk);
+ }
+ return result;
+ }
+
+ /**
+ * Grants the necessary permissions to write the given parameters.
+ */
+ export function parameters(
+ ctx: Construct,
+ targetParameters: string[] | undefined,
+ role: IRole,
+ ) {
+ if (targetParameters === undefined) {
+ return;
+ }
+
+ const paramSlices = sliceParameters(targetParameters);
+
+ for (let i = 0; i < paramSlices.length; i++) {
+ const putPolicy = new ManagedPolicy(
+ ctx,
+ `SopsSecretParameterProviderManagedPolicyParameterAccess${i}`,
+ {
+ description:
+ 'Policy to grant parameter provider permissions to put parameter',
+ },
+ );
+ putPolicy.addStatements(
+ new PolicyStatement({
+ actions: ['ssm:PutParameter'],
+ resources: paramSlices[i].map(
+ (param) =>
+ `arn:aws:ssm:${Stack.of(ctx).region}:${
+ Stack.of(ctx).account
+ }:parameter${param.startsWith('/') ? param : `/${param}`}`,
+ ),
+ }),
+ );
+ role.addManagedPolicy(putPolicy);
+ }
+ }
+
+ /**
+ * Grants the necessary permissions to read the given asset from S3.
+ */
+ export function assetBucket(asset: Asset | undefined, target: IGrantable) {
+ if (asset === undefined) {
+ return;
+ }
+ asset.bucket.grantRead(target);
}
}
diff --git a/test/__snapshots__/permissions.test.ts.snap b/test/__snapshots__/permissions.test.ts.snap
new file mode 100644
index 00000000..3c64327c
--- /dev/null
+++ b/test/__snapshots__/permissions.test.ts.snap
@@ -0,0 +1,62 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`parameters 1 parameter - full snapshot 1`] = `
+Object {
+ "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3": Object {
+ "Properties": Object {
+ "Description": "Policy to grant parameter provider permissions to put parameter",
+ "Path": "/",
+ "PolicyDocument": Object {
+ "Statement": Array [
+ Object {
+ "Action": "ssm:PutParameter",
+ "Effect": "Allow",
+ "Resource": Object {
+ "Fn::Join": Array [
+ "",
+ Array [
+ "arn:aws:ssm:",
+ Object {
+ "Ref": "AWS::Region",
+ },
+ ":",
+ Object {
+ "Ref": "AWS::AccountId",
+ },
+ ":parameter/parameter0",
+ ],
+ ],
+ },
+ },
+ ],
+ "Version": "2012-10-17",
+ },
+ },
+ "Type": "AWS::IAM::ManagedPolicy",
+ },
+}
+`;
+
+exports[`parameters 100 parameter - snapshot count 1`] = `
+Array [
+ "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess1E27103A2",
+]
+`;
+
+exports[`parameters 1000 parameter - snapshot count 1`] = `
+Array [
+ "SopsSecretParameterProviderManagedPolicyParameterAccess03C7CE5D3",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess1E27103A2",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess204C56AEE",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess39C2C7238",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess4810168FC",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess5A4020F4D",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess6D18916A5",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess7C1D2D5DD",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess881B83B3B",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess9F6C18C5F",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess10B80D3BA8",
+ "SopsSecretParameterProviderManagedPolicyParameterAccess118D826D5D",
+]
+`;
diff --git a/test/permissions.test.ts b/test/permissions.test.ts
new file mode 100644
index 00000000..e10ab9f1
--- /dev/null
+++ b/test/permissions.test.ts
@@ -0,0 +1,151 @@
+import * as fs from 'fs';
+import path from 'path';
+import { Stack } from 'aws-cdk-lib';
+import { Template } from 'aws-cdk-lib/assertions';
+import { IRole, Role } from 'aws-cdk-lib/aws-iam';
+import { Key } from 'aws-cdk-lib/aws-kms';
+import { Permissions } from '../src/SopsSync';
+
+describe('keysFromSopsContent', () => {
+ let stack: Stack;
+
+ beforeEach(() => {
+ stack = new Stack();
+ });
+
+ test('returns an empty array when no keys are found', () => {
+ const content = 'no keys here';
+ const result = Permissions.keysFromSopsContent(stack, content);
+ expect(result).toEqual([]);
+ });
+
+ test('returns an array of IKey objects when keys are found', () => {
+ const content = fs
+ .readFileSync(
+ path.join(__dirname, '../test-secrets/yaml/sopsfile.enc-kms.yaml'),
+ )
+ .toString();
+ const result = Permissions.keysFromSopsContent(stack, content);
+ expect(result).toHaveLength(1);
+ expect(result[0].keyArn).toBe(
+ 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab',
+ );
+ expect(result[0].keyId).toBe('00000000-1234-4321-abcd-1234abcd12ab');
+ });
+
+ test('returns multiple IKey objects when multiple keys are found', () => {
+ const content = fs
+ .readFileSync(
+ path.join(__dirname, '../test-secrets/yaml/sopsfile.enc-multikms.yaml'),
+ )
+ .toString();
+ const result = Permissions.keysFromSopsContent(stack, content);
+ expect(result).toHaveLength(4);
+ expect(result[0].keyArn).toBe(
+ 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab',
+ );
+ expect(result[0].keyId).toBe('00000000-1234-4321-abcd-1234abcd12ab');
+ expect(result[1].keyArn).toBe(
+ 'arn:aws:kms:aws-region-1:123456789011:key/00000001-1234-4321-abcd-1234abcd12ab',
+ );
+ expect(result[1].keyId).toBe('00000001-1234-4321-abcd-1234abcd12ab');
+ expect(result[2].keyArn).toBe(
+ 'arn:aws:kms:aws-region-1:123456789011:key/00000002-1234-4321-abcd-1234abcd12ab',
+ );
+ expect(result[2].keyId).toBe('00000002-1234-4321-abcd-1234abcd12ab');
+ expect(result[3].keyArn).toBe(
+ 'arn:aws:kms:aws-region-1:123456789011:key/00000003-1234-4321-abcd-1234abcd12ab',
+ );
+ expect(result[3].keyId).toBe('00000003-1234-4321-abcd-1234abcd12ab');
+ });
+});
+
+describe('keysFromSopsContentAlias', () => {
+ let stack: Stack;
+
+ beforeEach(() => {
+ stack = new Stack();
+ });
+
+ test('returns an empty array when no keys are found', () => {
+ const content = 'no keys here';
+ const result = Permissions.keysFromSopsContentAlias(stack, content);
+ expect(result).toEqual([]);
+ });
+
+ test('returns an array of IKey objects when keys are found', () => {
+ jest
+ .spyOn(Key, 'fromLookup')
+ .mockReturnValue(
+ Key.fromKeyArn(
+ stack,
+ 'Key',
+ 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab',
+ ),
+ );
+
+ const content = fs
+ .readFileSync(
+ path.join(
+ __dirname,
+ '../test-secrets/yaml/sopsfile.enc-kms-alias.yaml',
+ ),
+ )
+ .toString();
+ const result = Permissions.keysFromSopsContentAlias(stack, content);
+ expect(result[0].keyArn).toBe(
+ 'arn:aws:kms:aws-region-1:123456789011:key/00000000-1234-4321-abcd-1234abcd12ab',
+ );
+ expect(result[0].keyId).toBe('00000000-1234-4321-abcd-1234abcd12ab');
+ });
+});
+
+describe('parameters', () => {
+ let stack: Stack;
+ let role: IRole;
+
+ beforeEach(() => {
+ stack = new Stack();
+ role = Role.fromRoleArn(
+ stack,
+ 'TestRole',
+ 'arn:aws:iam::123456789012:role/test-role',
+ );
+ });
+
+ function genParameters(prefix: string, count: number) {
+ const params: string[] = [];
+ for (let i = 0; i < count; i++) {
+ params.push(`${prefix}${i}`);
+ }
+ return params;
+ }
+
+ test('1 parameter - full snapshot', () => {
+ const params = genParameters('parameter', 1);
+ Permissions.parameters(stack, params, role);
+ expect(
+ Template.fromStack(stack).findResources('AWS::IAM::ManagedPolicy'),
+ ).toMatchSnapshot();
+ });
+
+ test('100 parameter - snapshot count', () => {
+ const params = genParameters('parameter', 100);
+ Permissions.parameters(stack, params, role);
+ expect(
+ Object.keys(
+ Template.fromStack(stack).findResources('AWS::IAM::ManagedPolicy'),
+ ),
+ ).toMatchSnapshot();
+ });
+
+ test('1000 parameter - snapshot count', () => {
+ const params = genParameters('parameter', 1000);
+ Permissions.parameters(stack, params, role);
+ expect(
+ Object.keys(
+ Template.fromStack(stack).findResources('AWS::IAM::ManagedPolicy'),
+ ),
+ ).toMatchSnapshot();
+ });
+});
diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json
index a5efac8f..c412a32a 100644
--- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json
+++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.assets.json
@@ -1,15 +1,15 @@
{
"version": "36.0.0",
"files": {
- "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": {
+ "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": {
"source": {
- "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
@@ -79,7 +79,7 @@
}
}
},
- "e363e1c42928a35c199fd17c68fdac2df3407feb9216f8cdf1511a609a2b0f72": {
+ "403db9f2066ea2fc6a72ec5211cda6589c0049fadd0dd5ea36461a810960d709": {
"source": {
"path": "SecretIntegrationAsset.template.json",
"packaging": "file"
@@ -87,7 +87,7 @@
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "e363e1c42928a35c199fd17c68fdac2df3407feb9216f8cdf1511a609a2b0f72.json",
+ "objectKey": "403db9f2066ea2fc6a72ec5211cda6589c0049fadd0dd5ea36461a810960d709.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
diff --git a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json
index 40e8b4df..410ea70d 100644
--- a/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json
+++ b/test/secret-asset.integ.snapshot/SecretIntegrationAsset.template.json
@@ -31,8 +31,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -73,16 +72,6 @@
"Properties": {
"PolicyDocument": {
"Statement": [
- {
- "Action": [
- "secretsmanager:PutSecretValue",
- "secretsmanager:UpdateSecret"
- ],
- "Effect": "Allow",
- "Resource": {
- "Ref": "SopsSecretJSON72040543"
- }
- },
{
"Action": [
"s3:GetObject*",
@@ -124,6 +113,16 @@
}
]
},
+ {
+ "Action": [
+ "secretsmanager:PutSecretValue",
+ "secretsmanager:UpdateSecret"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Ref": "SopsSecretJSON72040543"
+ }
+ },
{
"Action": [
"secretsmanager:PutSecretValue",
@@ -232,7 +231,7 @@
"S3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
- "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip"
+ "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip"
},
"Environment": {
"Variables": {
@@ -285,8 +284,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -322,8 +320,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -359,8 +356,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -396,8 +392,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -433,8 +428,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -470,8 +464,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -507,8 +500,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -544,8 +536,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -581,8 +572,7 @@
"FlattenSeparator": ".",
"Format": "binary",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json
index f17b8c6a..cdd252e3 100644
--- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json
+++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.assets.json
@@ -1,20 +1,20 @@
{
"version": "36.0.0",
"files": {
- "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": {
+ "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": {
"source": {
- "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
- "3dcfa0a64e513705636a673bbca1b4c1ca9804d58579498b3479f581295035fa": {
+ "55aa34bad424010bf095d40fbfbbd4b7250f7c373c25a117c203aa788214e7a3": {
"source": {
"path": "SecretIntegrationInline.template.json",
"packaging": "file"
@@ -22,7 +22,7 @@
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "3dcfa0a64e513705636a673bbca1b4c1ca9804d58579498b3479f581295035fa.json",
+ "objectKey": "55aa34bad424010bf095d40fbfbbd4b7250f7c373c25a117c203aa788214e7a3.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
diff --git a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json
index 5ff61693..827a6514 100644
--- a/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json
+++ b/test/secret-inline.integ.snapshot/SecretIntegrationInline.template.json
@@ -29,8 +29,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -199,7 +198,7 @@
"S3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
- "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip"
+ "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip"
},
"Environment": {
"Variables": {
@@ -250,8 +249,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -285,8 +283,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -320,8 +317,7 @@
"FlattenSeparator": ".",
"Format": "dotenv",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -355,8 +351,7 @@
"FlattenSeparator": ".",
"Format": "dotenv",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -390,8 +385,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -425,8 +419,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -460,8 +453,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -495,8 +487,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -530,8 +521,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -565,8 +555,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json
index 3aa36317..d52b2917 100644
--- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json
+++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.assets.json
@@ -1,20 +1,20 @@
{
"version": "36.0.0",
"files": {
- "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": {
+ "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": {
"source": {
- "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
- "d4e0b1978d4b4a8bd4a3460f5250588a128ba18a716657ee533bbc19a93d6ce7": {
+ "d991538757fc85d0c7183933598d0517ea224a4d379c5cc40388ef064f255f29": {
"source": {
"path": "SecretIntegrationAsset.template.json",
"packaging": "file"
@@ -22,7 +22,7 @@
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "d4e0b1978d4b4a8bd4a3460f5250588a128ba18a716657ee533bbc19a93d6ce7.json",
+ "objectKey": "d991538757fc85d0c7183933598d0517ea224a4d379c5cc40388ef064f255f29.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
diff --git a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json
index 059453d4..32e01b29 100644
--- a/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json
+++ b/test/secret-manual.integ.snapshot/SecretIntegrationAsset.template.json
@@ -29,8 +29,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -73,7 +72,7 @@
"S3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
- "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip"
+ "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip"
},
"Environment": {
"Variables": {
@@ -123,8 +122,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -158,8 +156,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -193,8 +190,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -228,8 +224,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -263,8 +258,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -298,8 +292,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -333,8 +326,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -368,8 +360,7 @@
"FlattenSeparator": ".",
"Format": "yaml",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json
index 735fd0c3..86fe4c53 100644
--- a/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json
+++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.assets.json
@@ -1,15 +1,15 @@
{
"version": "36.0.0",
"files": {
- "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92": {
+ "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887": {
"source": {
- "path": "asset.b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "path": "asset.2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip",
+ "objectKey": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
@@ -27,7 +27,7 @@
}
}
},
- "b2891d8d1afc3baddc99715abe47f05ff84ad390166c5277a91fc50103fc4457": {
+ "6bf23bfa8222ed50eef10cf00cfb221343b0f056159410f55f8a28dc7c92b9b6": {
"source": {
"path": "SecretMultiKms.template.json",
"packaging": "file"
@@ -35,7 +35,7 @@
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
- "objectKey": "b2891d8d1afc3baddc99715abe47f05ff84ad390166c5277a91fc50103fc4457.json",
+ "objectKey": "6bf23bfa8222ed50eef10cf00cfb221343b0f056159410f55f8a28dc7c92b9b6.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
diff --git a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json
index 9a9960ee..1b9cd2fb 100644
--- a/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json
+++ b/test/secret-multikms.integ.snapshot/SecretMultiKms.template.json
@@ -191,8 +191,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
@@ -233,31 +232,6 @@
"Properties": {
"PolicyDocument": {
"Statement": [
- {
- "Action": [
- "secretsmanager:PutSecretValue",
- "secretsmanager:UpdateSecret"
- ],
- "Effect": "Allow",
- "Resource": {
- "Ref": "SopsSecretOwnKmsMey0B320436"
- }
- },
- {
- "Action": [
- "kms:Decrypt",
- "kms:Encrypt",
- "kms:ReEncrypt*",
- "kms:GenerateDataKey*"
- ],
- "Effect": "Allow",
- "Resource": {
- "Fn::GetAtt": [
- "CustomKey1E6D0D07",
- "Arn"
- ]
- }
- },
{
"Action": [
"s3:GetObject*",
@@ -299,6 +273,21 @@
}
]
},
+ {
+ "Action": [
+ "kms:Decrypt",
+ "kms:Encrypt",
+ "kms:ReEncrypt*",
+ "kms:GenerateDataKey*"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Fn::GetAtt": [
+ "CustomKey1E6D0D07",
+ "Arn"
+ ]
+ }
+ },
{
"Action": [
"secretsmanager:PutSecretValue",
@@ -306,7 +295,7 @@
],
"Effect": "Allow",
"Resource": {
- "Ref": "SopsSecretForeignKmsMey8C3BA0B7"
+ "Ref": "SopsSecretOwnKmsMey0B320436"
}
},
{
@@ -318,6 +307,16 @@
],
"Effect": "Allow",
"Resource": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
+ },
+ {
+ "Action": [
+ "secretsmanager:PutSecretValue",
+ "secretsmanager:UpdateSecret"
+ ],
+ "Effect": "Allow",
+ "Resource": {
+ "Ref": "SopsSecretForeignKmsMey8C3BA0B7"
+ }
}
],
"Version": "2012-10-17"
@@ -337,7 +336,7 @@
"S3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
- "S3Key": "b84f4ae044484433485a09ba2d15bdd1cd134698fecd0af024e8a36b2c001d92.zip"
+ "S3Key": "2c430c0ffff3fd5da4b853228c31f277ebb757a3e9a3f5e11321eb0cd8c91887.zip"
},
"Environment": {
"Variables": {
@@ -391,8 +390,7 @@
"FlattenSeparator": ".",
"Format": "json",
"StringifiedValues": true,
- "ResourceType": "SECRET",
- "CreationType": "SINGLE"
+ "ResourceType": "SECRET"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
diff --git a/test/secret.test.ts b/test/secret.test.ts
index 7797589e..92cfcdcd 100644
--- a/test/secret.test.ts
+++ b/test/secret.test.ts
@@ -506,7 +506,7 @@ test('Allowed options for SopsSync', () => {
sopsS3Key: 'test',
}),
).toThrowError(
- 'You have to specify both sopsS3Bucket and sopsS3Key or neither!',
+ 'You can either specify sopsFilePath or sopsS3Bucket and sopsS3Key!',
);
expect(
() =>
@@ -514,7 +514,7 @@ test('Allowed options for SopsSync', () => {
sopsS3Bucket: 'test',
}),
).toThrowError(
- 'You have to specify both sopsS3Bucket and sopsS3Key or neither!',
+ 'You can either specify sopsFilePath or sopsS3Bucket and sopsS3Key!',
);
expect(
() =>