Repository: udondan/cdk-ssm-document (language: python)

Issue:
BUG: Package does not work with latest version of `aws-cdk-lib`
When I try to run `cdk synth` or `cdk deploy`, I get the following error.
```
> "cdk synth"

/Users/tim/workplace/ma-mono/common/temp/node_modules/.pnpm/cdk-iam-floyd@0.300.0_ff8e2a29703a7cffac9ed3bbb4473395/node_modules/cdk-iam-floyd/lib/shared/policy-statement/6-principals.js:29
        this.principals = {};
                        ^

TypeError: Cannot set property principals of [object Object] which has only a getter
    at new PolicyStatementWithPrincipal (/Users/tim/workplace/ma-mono/common/temp/node_modules/.pnpm/cdk-iam-floyd@0.300.0_ff8e2a29703a7cffac9ed3bbb4473395/node_modules/cdk-iam-floyd/lib/shared/policy-statement/6-principals.js:29:25)
```

This is what my `package.json` looks like
```
 "dependencies": {
    "aws-cdk-lib": "^2.0.0",
    "constructs": "^10.0.0",
    "cdk-ssm-document": "^3.1.0",
    "cdk-iam-floyd": "^0.300.0",
  },
  ```
  
  This is what my code looks like
  ```
    private _createSMDocuments(): void {
    const cfnDoc = new Document(this._stack, 'SSMDoc', {
      name: `${this._stack.stackName}-SSMDoc`,
      documentType: 'Automation',
      content: fs
        .readFileSync(join(__dirname, `../../src/SSM.yaml`), 'utf8')
        .toString()
    });
  }
  ```
  
 If I set `aws-cdk-lib` to be explicitly `2.0.0`, I no longer get the error.

```
 "dependencies": {
    "aws-cdk-lib": "2.0.0",
    "constructs": "^10.0.0",
    "cdk-ssm-document": "^3.1.0",
    "cdk-iam-floyd": "^0.300.0",
  },
  ```
  

chore: Update dependencies for ckd-iam-floyd
The current version of `cdk-ssm-document` fails to build, because of the issue outlined [here](https://github.com/udondan/cdk-ssm-document/issues/29#issuecomment-1097685908).

This PR updates the peer dependency.

**Testing** 
Before the update running `npm run build` resulted in this error.
```
~/workspace/cdk-ssm-document: (master) $ yarn build
yarn run v1.15.2
$ jsii
[2022-04-13T22:39:00.057] [ERROR] jsii/compiler - Compilation errors prevented the JSII assembly from being created
warning JSII6: A "peerDependency" on "aws-cdk-lib" at "^2.0.0" means you should take a "devDependency" on "aws-cdk-lib" at "2.0.0" (found "^2.0.0")"
warning JSII6: A "peerDependency" on "cdk-iam-floyd" at "^0.300.0" means you should take a "devDependency" on "cdk-iam-floyd" at "0.300.0" (found "^0.300.0")"
warning JSII6: A "peerDependency" on "constructs" at "^10.0.0" means you should take a "devDependency" on "constructs" at "10.0.0" (found "^10.0.0")"
lib/index.ts:22:12 - warning JSII5018: "type" is a reserved word in Go. Using this name may cause problems when generating language bindings. Consider a different name.

22   readonly type: string;
              ~~~~
lib/index.ts:32:12 - warning JSII5018: "default" is a reserved word in C#, Java, Go. Using this name may cause problems when generating language bindings. Consider a different name.

32   readonly default?: any;
              ~~~~~~~
lib/index.ts:208:19 - warning JSII5018: "lambda" is a reserved word in Python. Using this name may cause problems when generating language bindings. Consider a different name.

208   public readonly lambda: lambda.IFunction;
                      ~~~~~~
lib/index.ts:270:9 - error TS2322: Type 'Ssm' is not assignable to type 'PolicyStatement'.
  Property 'principals' is protected in type 'Ssm' but public in type 'PolicyStatement'.

270         new statement.Ssm().allow().toListDocuments().toListTagsForResource(),
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/index.ts:271:9 - error TS2322: Type 'Ssm' is not assignable to type 'PolicyStatement'.

271         new statement.Ssm()
            ~~~~~~~~~~~~~~~~~~~
272           .allow()
    ~~~~~~~~~~~~~~~~~~
... 
274           .toAddTagsToResource()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275           .ifAwsRequestTag(createdByTag, ID),
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lib/index.ts:276:9 - error TS2322: Type 'Ssm' is not assignable to type 'PolicyStatement'.

276         new statement.Ssm()
            ~~~~~~~~~~~~~~~~~~~
277           .allow()
    ~~~~~~~~~~~~~~~~~~
... 
286           .toRemoveTagsFromResource()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
287           .ifResourceTag(createdByTag, ID),
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
node_modules/cdk-iam-floyd/lib/shared/policy-statement/6-principals.d.ts:29:15 - error TS2416: Property 'principals' in type 'PolicyStatementWithPrincipal' is not assignable to the same property in base type 'PolicyStatementWithEffect'.
  Type 'Principals' is missing the following properties from type 'IPrincipal[]': length, pop, push, concat, and 29 more.

29     protected principals: Principals;
                 ~~~~~~~~~~
test/lib/test-stack.ts:120:7 - error TS2345: Argument of type 'S3' is not assignable to parameter of type 'PolicyStatement'.
  Property 'principals' is protected in type 'S3' but public in type 'PolicyStatement'.

120       new statement.S3() //
          ~~~~~~~~~~~~~~~~~~~~~
121         .allow()
    ~~~~~~~~~~~~~~~~
122         .toGetObject()
    ~~~~~~~~~~~~~~~~~~~~~~
123         .onObject(bucket.bucketName, '*')
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
After the updates in this PR, running the build command results in no errors.


Discussion / hints:
Hi Tim,

the problem is related to iam-floyd. CDK 2.20.0 introduced a change that was conflicting with an internal floyd class property. This is fixed with [cdk-iam-floyd v0.351.0](https://github.com/udondan/iam-floyd/releases/tag/v0.351.0). You should be able it fix the issue by updating the version in your package.json.

I also have planned to remove the dependency to floyd - but not sure when I will find the time to do so.

Cheers,
Daniel


Hi Daniel,

Do you need to publish a new version of `cdk-ssm-document`. When I tried to use `cdk-iam-floyd v 0.351.0`, I get this error
```
└─┬ cdk-ssm-document
  └── ✕ unmet peer cdk-iam-floyd@^0.300.0: found 0.351.0
```

-Tim
@udondan I believe this fixes the issue. Can you take a look?
https://github.com/udondan/cdk-ssm-document/pull/30


Fix patch:
diff --git a/.gitignore b/.gitignore
index deadeef..d5fc74c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ lambda/*
 **/*.d.ts
 **/cdk.out
 **/package-lock.json
+tsconfig.tsbuildinfo
diff --git a/README.md b/README.md
index bb41ae6..2883b3b 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,6 @@ For TypeScript/NodeJS, add these to your `dependencies` in `package.json`. For P
 
 - cdk-ssm-document
 - aws-cdk-lib (^2.0.0)
-- cdk-iam-floyd (^0.300.0)
 - constructs (^10.0.0)
 
 ## CDK compatibility
@@ -140,41 +139,39 @@ export class TestStack extends cdk.Stack {
 ### Creating a distributor package
 
 ```typescript
-import * as cdk from 'aws-cdk-lib';
-import { Construct } from 'constructs';
+import { aws_iam, aws_s3, aws_s3_deployment, Stack, StackProps } from 'aws-cdk-lib';
 import { Document } from 'cdk-ssm-document';
-import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
-import * as s3 from 'aws-cdk-lib/aws-s3';
-import * as statement from 'cdk-iam-floyd';
+import { Construct } from 'constructs';
 import fs = require('fs');
 import path = require('path');
 
-
-export class TestStack extends cdk.Stack {
-  constructor(scope: cdk.Construct, id: string, props: cdk.StackProps) {
+export class TestStack extends Stack {
+  constructor(scope: Construct, id: string, props: StackProps) {
     super(scope, id, props);
 
-    const bucketName = `${cdk.Stack.of(this).account}-cdk-ssm-document-storage`;
-    const bucket = new s3.Bucket(this, 'DistributorPackages', {
+    const bucketName = `${Stack.of(this).account}-cdk-ssm-document-storage`;
+    const bucket = new aws_s3.Bucket(this, 'DistributorPackages', {
       bucketName: bucketName,
     });
-    const packageDeploy = new s3deploy.BucketDeployment(this, 'distribution-packages', {
-      sources: [s3deploy.Source.asset('../location/to/distributor/packages')],
-      destinationBucket: bucket
-    });
+    const packageDeploy = new aws_s3_deployment.BucketDeployment(
+      this,
+      'distribution-packages',
+      {
+        sources: [aws_s3_deployment.Source.asset('../location/to/distributor/packages')],
+        destinationBucket: bucket,
+      }
+    );
 
-    file = path.join(
+    const file = path.join(
       __dirname,
       '../location/to/distributor/packages/v1/manifest.json'
     );
-    const docE = new Document(this, `SSM-Distribution-Package`, {
+    const doc = new Document(this, `SSM-Distribution-Package`, {
       documentType: 'Package',
       name: 'Test-Distribution-Package',
       content: fs.readFileSync(file).toString(),
       versionName: '1.0-Custom-Name',
-      attachments: [
-        { key: "SourceUrl", values: [`s3://${bucketName}/v1`] }
-      ]
+      attachments: [{ key: 'SourceUrl', values: [`s3://${bucketName}/v1`] }],
     });
 
     /**
@@ -193,14 +190,15 @@ export class TestStack extends cdk.Stack {
      * ```
      */
     doc.lambda.role?.addToPrincipalPolicy(
-      new statement.S3() //
-        .allow()
-        .toGetObject()
-        .onObject(bucket.bucketName, '*')
+      new aws_iam.PolicyStatement({
+        actions: ['s3:GetObject'],
+        resources: [`${bucket.arnForObjects('*')}`],
+      })
     );
     doc.node.addDependency(packageDeploy);
   }
 }
+
 ```
 
 ## Deploying many documents in a single stack
diff --git a/VERSION b/VERSION
index fd2a018..94ff29c 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-3.1.0
+3.1.1
diff --git a/lib/index.ts b/lib/index.ts
index 1e27981..d2b8789 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -1,7 +1,16 @@
-import cdk = require('aws-cdk-lib');
-import iam = require('aws-cdk-lib/aws-iam');
-import lambda = require('aws-cdk-lib/aws-lambda');
-import * as statement from 'cdk-iam-floyd';
+import {
+  Annotations,
+  aws_iam,
+  aws_lambda,
+  CustomResource,
+  Duration,
+  ITaggable,
+  Lazy,
+  Stack,
+  StackProps,
+  TagManager,
+  TagType,
+} from 'aws-cdk-lib';
 import { Construct } from 'constructs';
 import yaml = require('js-yaml');
 import path = require('path');
@@ -143,7 +152,7 @@ export interface DocumentContent {
 /**
  * Definition of the SSM document
  */
-export interface DocumentProps extends cdk.StackProps {
+export interface DocumentProps extends StackProps {
   /**
    * Defines if the default version should be updated to the latest version on document updates
    *
@@ -191,7 +200,7 @@ export interface DocumentProps extends cdk.StackProps {
 /**
  * An SSM document
  */
-export class Document extends Construct implements cdk.ITaggable {
+export class Document extends Construct implements ITaggable {
   /**
    * Name of the document
    */
@@ -200,12 +209,12 @@ export class Document extends Construct implements cdk.ITaggable {
   /**
    * Resource tags
    */
-  public readonly tags: cdk.TagManager;
+  public readonly tags: TagManager;
 
   /**
    * The lambda function that is created
    */
-  public readonly lambda: lambda.IFunction;
+  public readonly lambda: aws_lambda.IFunction;
 
   /**
    * Defines a new SSM document
@@ -213,15 +222,15 @@ export class Document extends Construct implements cdk.ITaggable {
   constructor(scope: Construct, id: string, props: DocumentProps) {
     super(scope, id);
 
-    this.tags = new cdk.TagManager(cdk.TagType.MAP, 'Custom::SSM-Document');
+    this.tags = new TagManager(TagType.MAP, 'Custom::SSM-Document');
     this.tags.setTag(createdByTag, ID);
 
-    const stack = cdk.Stack.of(this).stackName;
+    const stack = Stack.of(this).stackName;
     this.lambda = this.ensureLambda();
     const name = this.fixDocumentName(props.name);
 
     if (name.length < 3 || name.length > 128) {
-      cdk.Annotations.of(this).addError(
+      Annotations.of(this).addError(
         `SSM Document name ${name} is invalid. The name must be between 3 and 128 characters.`
       );
       return;
@@ -233,7 +242,7 @@ export class Document extends Construct implements cdk.ITaggable {
       content = yaml.safeLoad(content) as DocumentContent;
     }
 
-    const document = new cdk.CustomResource(this, `SSM-Document-${name}`, {
+    const document = new CustomResource(this, `SSM-Document-${name}`, {
       serviceToken: this.lambda.functionArn,
       resourceType: resourceType,
       properties: {
@@ -245,7 +254,7 @@ export class Document extends Construct implements cdk.ITaggable {
         attachments: props.attachments,
         versionName: props.versionName,
         StackName: stack,
-        tags: cdk.Lazy.any({
+        tags: Lazy.any({
           produce: () => this.tags.renderTags(),
         }),
       },
@@ -255,59 +264,79 @@ export class Document extends Construct implements cdk.ITaggable {
     this.name = document.getAttString('Name');
   }
 
-  private ensureLambda(): lambda.Function {
-    const stack = cdk.Stack.of(this);
+  private ensureLambda(): aws_lambda.Function {
+    const stack = Stack.of(this);
     const constructName = 'SSM-Document-Manager-Lambda';
     const existing = stack.node.tryFindChild(constructName);
     if (existing) {
-      return existing as lambda.Function;
+      return existing as aws_lambda.Function;
     }
 
-    const policy = new iam.ManagedPolicy(stack, 'SSM-Document-Manager-Policy', {
-      managedPolicyName: `${stack.stackName}-${cleanID}`,
-      description: `Used by Lambda ${cleanID}, which is a custom CFN resource, managing SSM documents`,
-      statements: [
-        new statement.Ssm().allow().toListDocuments().toListTagsForResource(),
-        new statement.Ssm()
-          .allow()
-          .toCreateDocument()
-          .toAddTagsToResource()
-          .ifAwsRequestTag(createdByTag, ID),
-        new statement.Ssm()
-          .allow()
-          .toDeleteDocument()
-          .toDescribeDocument()
-          .toGetDocument()
-          .toListDocumentVersions()
-          .toModifyDocumentPermission()
-          .toUpdateDocument()
-          .toUpdateDocumentDefaultVersion()
-          .toAddTagsToResource()
-          .toRemoveTagsFromResource()
-          .ifResourceTag(createdByTag, ID),
-      ],
-    });
-
-    const role = new iam.Role(stack, 'SSM-Document-Manager-Role', {
+    const policy = new aws_iam.ManagedPolicy(
+      stack,
+      'SSM-Document-Manager-Policy',
+      {
+        managedPolicyName: `${stack.stackName}-${cleanID}`,
+        description: `Used by Lambda ${cleanID}, which is a custom CFN resource, managing SSM documents`,
+        statements: [
+          new aws_iam.PolicyStatement({
+            actions: ['ssm:ListDocuments', 'ssm:ListTagsForResource'],
+            resources: ['*'],
+          }),
+          new aws_iam.PolicyStatement({
+            actions: ['ssm:AddTagsToResource', 'ssm:CreateDocument'],
+            resources: ['*'],
+            conditions: {
+              StringLike: {
+                'aws:RequestTag/CreatedByCfnCustomResource': ID,
+              },
+            },
+          }),
+          new aws_iam.PolicyStatement({
+            actions: [
+              'ssm:AddTagsToResource',
+              'ssm:DeleteDocument',
+              'ssm:DescribeDocument',
+              'ssm:GetDocument',
+              'ssm:ListDocumentVersions',
+              'ssm:ModifyDocumentPermission',
+              'ssm:RemoveTagsFromResource',
+              'ssm:UpdateDocument',
+              'ssm:UpdateDocumentDefaultVersion',
+            ],
+            resources: ['*'],
+            conditions: {
+              StringLike: {
+                'ssm:ResourceTag/CreatedByCfnCustomResource': ID,
+              },
+            },
+          }),
+        ],
+      }
+    );
+
+    const role = new aws_iam.Role(stack, 'SSM-Document-Manager-Role', {
       roleName: `${stack.stackName}-${cleanID}`,
       description: `Used by Lambda ${cleanID}, which is a custom CFN resource, managing SSM documents`,
-      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
+      assumedBy: new aws_iam.ServicePrincipal('lambda.amazonaws.com'),
       managedPolicies: [
         policy,
-        iam.ManagedPolicy.fromAwsManagedPolicyName(
+        aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
           'service-role/AWSLambdaBasicExecutionRole'
         ),
       ],
     });
 
-    const fn = new lambda.Function(stack, constructName, {
+    const fn = new aws_lambda.Function(stack, constructName, {
       functionName: `${stack.stackName}-${cleanID}`,
       role: role,
       description: 'Custom CFN resource: Manage SSM Documents',
-      runtime: lambda.Runtime.NODEJS_14_X,
+      runtime: aws_lambda.Runtime.NODEJS_14_X,
       handler: 'index.handler',
-      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/code.zip')),
-      timeout: cdk.Duration.minutes(lambdaTimeout),
+      code: aws_lambda.Code.fromAsset(
+        path.join(__dirname, '../lambda/code.zip')
+      ),
+      timeout: Duration.minutes(lambdaTimeout),
     });
 
     return fn;
diff --git a/package.json b/package.json
index 8f59a6a..fc8c15d 100644
--- a/package.json
+++ b/package.json
@@ -60,11 +60,10 @@
     "aws-lambda": "^1.0.7",
     "aws-sdk": "^2.1069.0",
     "constructs": "^10.0.0",
-    "jsii": "^1.52.1",
-    "jsii-pacmak": "^1.52.1",
+    "jsii": "1.55.1",
+    "jsii-pacmak": "1.55.1",
     "ts-node": "^10.4.0",
     "typescript": "^4.5.5",
-    "cdk-iam-floyd": "^0.300.0",
     "aws-cloudformation-custom-resource": "^2.0.1"
   },
   "dependencies": {
@@ -72,7 +71,6 @@
   },
   "peerDependencies": {
     "aws-cdk-lib": "^2.0.0",
-    "cdk-iam-floyd": "^0.300.0",
     "constructs": "^10.0.0"
   },
   "bundleDependencies": [


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f370c27..54b04b2 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -31,13 +31,7 @@ jobs:
           chmod 777 $GITHUB_WORKSPACE/lambda --recursive
 
       - name: Deploy
-        run: cd test && make deploy
-        env:
-          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
-          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
-
-      - name: Destroy
-        run: cd test && make DESTROY
+        run: cd test && make deploy DESTROY
         env:
           AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
           AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
diff --git a/test/lib/test-stack.ts b/test/lib/test-stack.ts
index 392d026..f1ccf8b 100644
--- a/test/lib/test-stack.ts
+++ b/test/lib/test-stack.ts
@@ -1,15 +1,12 @@
-import * as cdk from 'aws-cdk-lib';
-import * as s3 from 'aws-cdk-lib/aws-s3';
-import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
-import * as statement from 'cdk-iam-floyd';
+import { aws_iam, aws_s3, aws_s3_deployment, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
 import { Construct } from 'constructs';
 import fs = require('fs');
 import path = require('path');
 
 import { Document } from '../../lib';
 
-export class TestStack extends cdk.Stack {
-  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
+export class TestStack extends Stack {
+  constructor(scope: Construct, id: string, props?: StackProps) {
     super(scope, id, props);
     let file = path.join(
       __dirname,
@@ -68,21 +65,23 @@ export class TestStack extends cdk.Stack {
      *
      * Requires a bucket to hold install/update/uninstall scripts.
      */
-    const bucketName = `${cdk.Stack.of(this).account}-cdk-ssm-document-storage`;
-    const bucket = new s3.Bucket(this, 'DistributorPackages', {
+    const bucketName = `${Stack.of(this).account}-cdk-ssm-document-storage`;
+    const bucket = new aws_s3.Bucket(this, 'DistributorPackages', {
       bucketName: bucketName,
-      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
+      blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL,
       enforceSSL: true,
-      encryption: s3.BucketEncryption.KMS_MANAGED,
+      encryption: aws_s3.BucketEncryption.KMS_MANAGED,
       // Makes for easy destroy and rerun of this stack over and over.
-      removalPolicy: cdk.RemovalPolicy.DESTROY,
+      removalPolicy: RemovalPolicy.DESTROY,
       autoDeleteObjects: true,
     });
-    const packageDeploy = new s3deploy.BucketDeployment(
+    const packageDeploy = new aws_s3_deployment.BucketDeployment(
       this,
       'distribution-packages',
       {
-        sources: [s3deploy.Source.asset('../test/documents/distributor')],
+        sources: [
+          aws_s3_deployment.Source.asset('../test/documents/distributor'),
+        ],
         destinationBucket: bucket,
       }
     );
@@ -117,10 +116,10 @@ export class TestStack extends cdk.Stack {
      * `Active`.
      */
     docE.lambda.role?.addToPrincipalPolicy(
-      new statement.S3() //
-        .allow()
-        .toGetObject()
-        .onObject(bucket.bucketName, '*')
+      new aws_iam.PolicyStatement({
+        actions: ['s3:GetObject'],
+        resources: [`${bucket.arnForObjects('*')}`],
+      })
     );
 
     docE.node.addDependency(docD);
diff --git a/test/package.json b/test/package.json
index 1e25e60..60d9d70 100644
--- a/test/package.json
+++ b/test/package.json
@@ -15,7 +15,6 @@
   },
   "dependencies": {
     "aws-cdk-lib": "2.x",
-    "cdk-iam-floyd": "0.313.0",
     "cdk-ssm-document": "file:.."
   }
 }