The python aws_config.CfnCustomRule construct's __init__() method improperly passes the construct's "scope" property to the jsii.create() method. It should be pass the property "scope_".
scratch.py
from aws_cdk import core, aws_config
class ConfigRuleStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id)
aws_config.CfnConfigRule(
self, "myRule",
source=aws_config.CfnConfigRule.SourceProperty(
owner="AWS",
source_identifier="REQUIRED_TAGS"
),
scope=aws_config.CfnConfigRule.ScopeProperty(
compliance_resource_types=["AWS::EC2::Volume"]
)
)
app = core.App()
ConfigRuleStack(app, "rules-stack")
app.synth()
$ cdk synth -a "python3 scratch.py"
jsii.errors.JavaScriptError:
Error: Expected object reference, got {"complianceResourceId":null,"complianceResourceTypes":["AWS::EC2::Volume"],"tagKey":null,"tagValue":null}
at Object.deserialize (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:12380:23)
at Kernel._toSandbox (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7159:61)
at /home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7212:33
at Array.map (<anonymous>)
at Kernel._boxUnboxParameters (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7212:19)
at Kernel._toSandboxValues (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7197:21)
at /home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:6798:66
at Kernel._wrapSandboxCode (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:7253:19)
at Kernel._create (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:6798:26)
at Kernel.create (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:6552:21)
at KernelHost.processRequest (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:6327:28)
at KernelHost.run (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:6270:14)
at Immediate._onImmediate (/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_embedded/jsii/jsii-runtime.js:6273:37)
at processImmediate (internal/timers.js:439:21)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "scratch.py", line 20, in <module>
ConfigRuleStack(app, "rules-stack")
File "/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_runtime.py", line 66, in __call__
inst = super().__call__(*args, **kwargs)
File "scratch.py", line 15, in __init__
compliance_resource_types=["AWS::EC2::Volume"]
File "/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_runtime.py", line 66, in __call__
inst = super().__call__(*args, **kwargs)
File "/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/aws_cdk/aws_config/__init__.py", line 153, in __init__
jsii.create(CfnConfigRule, self, [scope, id, props])
File "/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_kernel/__init__.py", line 208, in create
overrides=overrides,
File "/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_kernel/providers/process.py", line 331, in create
return self._process.send(request, CreateResponse)
File "/home/username/Projects/foo/foo-aws-config/.venv/lib/python3.6/site-packages/jsii/_kernel/providers/process.py", line 316, in send
raise JSIIError(resp.error) from JavaScriptError(resp.stack)
jsii.errors.JSIIError: Expected object reference, got {"complianceResourceId":null,"complianceResourceTypes":["AWS::EC2::Volume"],"tagKey":null,"tagValue":null}
Subprocess exited with error 1
Most Construct classes take a positional argument called "scope" which represents the parent core.Construct object.
But in the in the CfnConfigRule class (lib/python3.6/site-packages/aws_cdk/aws_config/__init__.py), the positional argument "scope" is smartly changed to "scope_". It is likely because this class also has a named parameter "scope".
The problem is that on line 153 we call jsii.create(CfnConfigRule, self, [scope, id, props]) and pass the "scope" parameter (which is an IResolveable or a ScopeProperty object type) when we should be passing the scope_ parameter (which is a core.Construct) like this jsii.create(CfnConfigRule, self, [scope_, id, props]).
As such, it throws the error.
lib/python3.6/site-packages/aws_cdk/aws_config/__init__.py
starting at line 130:
class CfnConfigRule(aws_cdk.core.CfnResource, metaclass=jsii.JSIIMeta, jsii_type="@aws-cdk/aws-config.CfnConfigRule"):
"""A CloudFormation ``AWS::Config::ConfigRule``.
see
:see: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-config-configrule.html
cloudformationResource:
:cloudformationResource:: AWS::Config::ConfigRule
"""
def __init__(self, scope_: aws_cdk.core.Construct, id: str, *, source: typing.Union["SourceProperty", aws_cdk.core.IResolvable], config_rule_name: typing.Optional[str]=None, description: typing.Optional[str]=None, input_parameters: typing.Any=None, maximum_execution_frequency: typing.Optional[str]=None, scope: typing.Optional[ typing.Union[ typing.Optional[aws_cdk.core.IResolvable], typing.Optional["ScopeProperty"] ] ]=None ) -> None:
"""Create a new ``AWS::Config::ConfigRule``.
:param scope: - scope in which this resource is defined.
:param id: - scoped id of the resource.
:param props: - resource properties.
:param source: ``AWS::Config::ConfigRule.Source``.
:param config_rule_name: ``AWS::Config::ConfigRule.ConfigRuleName``.
:param description: ``AWS::Config::ConfigRule.Description``.
:param input_parameters: ``AWS::Config::ConfigRule.InputParameters``.
:param maximum_execution_frequency: ``AWS::Config::ConfigRule.MaximumExecutionFrequency``.
:param scope: ``AWS::Config::ConfigRule.Scope``.
"""
props = CfnConfigRuleProps(source=source, config_rule_name=config_rule_name, description=description, input_parameters=input_parameters, maximum_execution_frequency=maximum_execution_frequency, scope=scope)
jsii.create(CfnConfigRule, self, [scope, id, props])
This is :bug: Bug Report
As a workaround, can you try with the ManagedRule class and the scopeToResource()/scopeToTag() methods?
@jofoid, I demonstrated the problem in this issue using a managed rule because the code was simpler than a custom rule. I actually need to define a custom rule with the lambda hosted in a remote account.
I would use the aws_config.CustomRule construct, which works using the method you mentioned. But it requires an ILambda object for the lambda= parameter. In my case, the lambda is in a remote account and I need to provide an arn to the lambda rather than a lambda object... So I was forced to use the CfnConfigRule construct and ran into this bug with the scope= parameter.
The workaround I'm currently using is the core.CfnResource construct.
@peterb154 you should be able to use the CustomRule construct and import your existing Lambda function using the from_function_arn class method to get an IFunction object.
That works! Thanks!
For others looking for an example of how to do this.
```
my_lambda = aws_lambda.Function.from_function_arn(
self, "myLambda",
function_arn=f"arn:aws:lambda:{lambda_region}:{lambda_account}:function:{lambda_name}"
)
my_rule = aws_config.CustomRule(
self, "myRule",
lambda_function=my_lambda,
configuration_changes=True,
)
remote_rule.scope_to_resource("AWS::EC2::VPC")
````
Most helpful comment
That works! Thanks!
For others looking for an example of how to do this.
```
my_lambda = aws_lambda.Function.from_function_arn(
self, "myLambda",
function_arn=f"arn:aws:lambda:{lambda_region}:{lambda_account}:function:{lambda_name}"
)
my_rule = aws_config.CustomRule(
self, "myRule",
lambda_function=my_lambda,
configuration_changes=True,
)
remote_rule.scope_to_resource("AWS::EC2::VPC")
````