How do I attach a "PolicyDocument" object to a TopicPolicy using the AWS CDK?

Viewed 3299

Forgive me, this is my first time asking a question on stack overflow. I will try and make this as clear as possible.

The Problem:

I'm getting this command line error when trying to synthesize a stack using the AWS CDK. I'm utilizing C# as my language and just playing around on my local machine.

It seems that I am not passing a "policy document" object when constructing my topic policy object based on this documentation. In the documentation, under "CfnTopicPolicyProps (interface)" it shows both a topics object and a policyDocument object can be included. However, in Visual Studio when I look at the definition for the interface it only includes the topics object. I included my use of the interface below and the definitions for some of the classes, interfaces and their parents. I'm a relatively new developer but I'm completely at a loss for an explanation. What am I missing here?

Thank you all in advance for any help or advice you can provide.

CLI Error Output

C:\MyRepository\awsCdkApp1>cdk synth Test-Stack-1
Unhandled Exception: Amazon.JSII.Runtime.JsiiException: While synthesizing Test-Stack-1/topicPolicy/Resource: Supplied properties not correct for "CfnTopicPolicyProps"
  policyDocument: required but missing
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Deputy.DeputyBase.<InvokeMethodCore>g__GetResult|18_0[T](<>c__DisplayClass18_0`1& )
   at Amazon.JSII.Runtime.Deputy.DeputyBase.InvokeMethodCore[T](JsiiMethodAttribute methodAttribute, Object[] arguments, Func`3 beginFunc, Func`3 invokeFunc)
   at Amazon.JSII.Runtime.Deputy.DeputyBase.InvokeInstanceMethod[T](Object[] arguments, String methodName)
   at Amazon.CDK.App.Run()
   at HelloCdk.Program.Main(String[] args) in C:\MyRepository/Program.cs:line 19
(node:16396) UnhandledPromiseRejectionWarning: Error: EPIPE: broken pipe, write
    at Object.writeSync (fs.js:569:3)
    at t.SyncStdio.writeBuffer (C:\Users\username\AppData\Local\Temp\4k3fctka.rty\jsii-runtime.js:1:165352)
    at t.SyncStdio.writeLine (C:\Users\username\AppData\Local\Temp\4k3fctka.rty\jsii-runtime.js:1:164892)
    at t.InputOutput.write (C:\Users\username\AppData\Local\Temp\4k3fctka.rty\jsii-runtime.js:1:164341)
    at t.KernelHost.writeError (C:\Users\username\AppData\Local\Temp\4k3fctka.rty\jsii-runtime.js:1:79440)
    at i.then.catch.e (C:\Users\username\AppData\Local\Temp\4k3fctka.rty\jsii-runtime.js:1:79101)
(node:16396) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:16396) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Subprocess exited with error 3762504530

Utilization of the interface:

var topic = new Topic(this, "myTopic", new TopicProps
        {
            DisplayName = "myTopic",
            TopicName = "myTopic"
        });

        var topicRef = TopicRef.Import(this, "reference", new TopicRefProps
        {
            TopicArn = topic.TopicArn,
            TopicName = topic.TopicName,
        });

        var policyStatement1 = new PolicyStatement(0);
        policyStatement1.Describe("Default");
        policyStatement1.AddAwsPrincipal("*");
        policyStatement1.AddActions("- SNS:Publish\n " +
                                   "- SNS:RemovePermission\n" +
                                   "- SNS:SetTopicAttributes\n" +
                                   "- SNS:DeleteTopic\n" +
                                   "- SNS:ListSubscriptionsByTopic\n" +
                                   "- SNS:GetTopicAttributes\n" +
                                   "- SNS:Receive\n" +
                                   "- SNS:AddPermission\n" +
                                   "- SNS:Subscribe\n");
        policyStatement1.AddCondition("StringEquals", "sOmEtHiNg");
        policyStatement1.AddResource(topic.TopicArn);

        var policyStatement2 = new PolicyStatement(0);
        policyStatement2.Describe("ProviderBucketAllow");
        policyStatement2.AddAwsPrincipal("*");
        policyStatement2.AddAction("- SNS:Publish");
        policyStatement2.AddCondition("StringEquals", @"sOmEtHiNg");
        policyStatement2.AddResource(topic.TopicArn);

        var policyDocument = new PolicyDocument("PolicyDocument");
        policyDocument.AddStatement(policyStatement1);
        policyDocument.AddStatement(policyStatement2);

        //Generate the topic policy resource
        var topicPolicy = new TopicPolicy(this, "topicPolicy", new TopicPolicyProps
        {
            //Here is where I would imagine I pass the policyDocument Object?
            //If I try to pass policyDocument, I get a ton of errors...
            Topics = new[] {topicRef}
        });

TopicRefProps Definition:

using Amazon.JSII.Runtime.Deputy;

namespace Amazon.CDK.AWS.SNS
{
    public class TopicRefProps : DeputyBase, ITopicRefProps
    {
        public TopicRefProps();

        [JsiiProperty("topicArn", "{\"primitive\":\"string\"}", true)]
        public string TopicArn { get; set; }
        [JsiiProperty("topicName", "{\"primitive\":\"string\"}", true)]
        public string TopicName { get; set; }
    }
}

ITopicRefProps Definition:

using Amazon.JSII.Runtime.Deputy;

namespace Amazon.CDK.AWS.SNS
{
    [JsiiInterface(typeof(ITopicRefProps), "@aws-cdk/aws-sns.TopicRefProps")]
    public interface ITopicRefProps
    {
        [JsiiProperty("topicArn", "{\"primitive\":\"string\"}", false)]
        string TopicArn { get; set; }
        [JsiiProperty("topicName", "{\"primitive\":\"string\"}", false)]
        string TopicName { get; set; }
    }
}

DeputyBase Definition:

using System;
using System.Runtime.CompilerServices;

namespace Amazon.JSII.Runtime.Deputy
{
    public abstract class DeputyBase
    {
        protected DeputyBase(DeputyProps props = null);
        protected DeputyBase(ByRefValue reference);

        public ByRefValue Reference { get; }

        protected static T GetStaticProperty<T>(Type type, [CallerMemberName] string propertyName = null);
        protected static T InvokeStaticMethod<T>(Type type, object[] arguments, [CallerMemberName] string methodName = null);
        protected static void InvokeStaticVoidMethod(Type type, object[] arguments, [CallerMemberName] string methodName = null);
        protected static void SetStaticProperty<T>(Type type, T value, [CallerMemberName] string propertyName = null);
        protected T GetInstanceProperty<T>([CallerMemberName] string propertyName = null);
        protected T InvokeInstanceMethod<T>(object[] arguments, [CallerMemberName] string methodName = null);
        protected void InvokeInstanceVoidMethod(object[] arguments, [CallerMemberName] string methodName = null);
        protected void SetInstanceProperty<T>(T value, [CallerMemberName] string propertyName = null);

        public sealed class DeputyProps
        {
            public DeputyProps(object[] arguments = null);

            public object[] Arguments { get; }
        }
    }
}
1 Answers

There's a couple of things here...

  1. The Topic class is a valid TopicRef, so you shouldn't have to do the TopicRef.import dance here.
  2. You can use Topic#addToResourcePolicy to add statements to your Topic's policy, instead of creating a TopicPolicy resource by yourself.

So your code could possibly end up something like:

var topic = new Topic(this, "myTopic", new TopicProps
{
    DisplayName = "myTopic",
    TopicName = "myTopic"
});

topic.AddToResourcePolicy(new PolicyStatement()
    .Describe("Default")
    .AddAwsPrincipal("*")
    .AddActions("sns:Publish",
                "sns:RemovePermission",
                "sns:SetTopicAttributes",
                "sns:DeleteTopic",
                "sns:ListSubscriptionsByTopic",
                "sns:GetTopicAttributes",
                "sns:Receive",
                "sns:AddPermission",
                "sns:Subscribe")
    .AddCondition("StringEquals", /* needs to be a map of condition key to value */);
topic.AddToResourcePolicy(new PolicyStatement()
    .Describe("ProviderBucketAllow")
    .AddAwsPrincipal("*")
    .AddAction("sns:Publish")
    .AddCondition("StringEquals", /* needs to be a map of condition key to value */);
Related