Skip to content

Commit

Permalink
Merge pull request #319 from awslabs/master
Browse files Browse the repository at this point in the history
RELEASE 0.8.2
  • Loading branch information
rickychau2780 authored Sep 10, 2021
2 parents 3bbcb6d + dc3c67e commit e2b09bb
Show file tree
Hide file tree
Showing 10 changed files with 659 additions and 26 deletions.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ include rdk/template/runtime/python3.7/*
include rdk/template/runtime/python3.7-lib/*
include rdk/template/runtime/python3.8/*
include rdk/template/runtime/python3.8-lib/*
include rdk/template/runtime/python3.9/*
include rdk/template/runtime/python3.9-lib/*
include rdk/template/runtime/dotnetcore1.0/*
include rdk/template/runtime/dotnetcore1.0/bin/*
include rdk/template/runtime/dotnetcore1.0/obj/*
Expand Down
10 changes: 5 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ For complete documentation, including command reference, check out the `ReadTheD

Getting Started
===============
Uses python 3.6/3.7/3.8 and is installed via pip. Requires you to have an AWS account and sufficient permissions to manage the Config service, and to create S3 Buckets, Roles, and Lambda Functions. An AWS IAM Policy Document that describes the minimum necessary permissions can be found at policy/rdk-minimum-permissions.json.
Uses python 3.6/3.7/3.8/3.9 and is installed via pip. Requires you to have an AWS account and sufficient permissions to manage the Config service, and to create S3 Buckets, Roles, and Lambda Functions. An AWS IAM Policy Document that describes the minimum necessary permissions can be found at policy/rdk-minimum-permissions.json.

Under the hood, rdk uses boto3 to make API calls to AWS, so you can set your credentials any way that boto3 recognizes (options 3 through 8 here: http://boto3.readthedocs.io/en/latest/guide/configuration.html) or pass them in with the command-line parameters --profile, --region, --access-key-id, or --secret-access-key

Expand Down Expand Up @@ -55,7 +55,7 @@ To use the RDK, it's recommended to create a directory that will be your working

Running ``init`` subsequent times will validate your AWS Config setup and re-create any S3 buckets or IAM resources that are needed.

- If you have config delievery bucket already present in some other AWS account then use **--config-bucket-exists-in-another-account** as argument:::
- If you have config delivery bucket already present in some other AWS account then use **--config-bucket-exists-in-another-account** as argument:::

$ rdk init --config-bucket-exists-in-another-account
- If you have AWS Organizations/ControlTower Setup in your AWS environment then additionally, use **--control-tower** as argument:::
Expand Down Expand Up @@ -162,7 +162,7 @@ The exact output will vary depending on Lambda runtime. You can use the --all f

Deploy Organization Rule
------------------------
You can also deploy the Rule to your AWS Orgnization using the ``deploy-organization`` command.
You can also deploy the Rule to your AWS Organization using the ``deploy-organization`` command.
For successful evaluation of custom rules in child accounts, please make sure you do one of the following:

1. Set ASSUME_ROLE_MODE in Lambda code to True, to get the lambda to assume the Role attached on the Config Service and confirm that the role trusts the master account where the Lambda function is going to be deployed.
Expand Down Expand Up @@ -204,7 +204,7 @@ You can use the ``-n`` and ``-f`` command line flags just like the UNIX ``tail``
Running the tests
=================

The `testing` directory contains scripts and buildspec files that I use to run basic functionality tests across a variety of CLI environemnts (currently Ubuntu linux running python 3.6/3.7/3.8, and Windows Server running python3.6). If there is interest I can release a CloudFormation template that could be used to build the test environment, let me know if this is something you want!
The `testing` directory contains scripts and buildspec files that I use to run basic functionality tests across a variety of CLI environments (currently Ubuntu linux running python 3.6/3.7/3.8/3.9, and Windows Server running python3.6). If there is interest I can release a CloudFormation template that could be used to build the test environment, let me know if this is something you want!


Advanced Features
Expand Down Expand Up @@ -242,7 +242,7 @@ It is now possible to define a resource type that is not yet supported by rdk. T
Custom Lambda Function Name
---------------------------
As of version 0.7.14, instead of defaulting the lambda function names to 'RDK-Rule-Function-<RULE_NAME>' it is possible to customize the name for lambda function to any 64 characters string as per lambda naming standrds using the optional '--custom-lambda-name' flag while performing rdk create. This opens up new features like :
As of version 0.7.14, instead of defaulting the lambda function names to 'RDK-Rule-Function-<RULE_NAME>' it is possible to customize the name for the Lambda function to any 64 characters string as per Lambda's naming standards using the optional '--custom-lambda-name' flag while performing rdk create. This opens up new features like :

1. Longer config rule name.
2. Custom lambda function naming as per personal or enterprise standards.
Expand Down
2 changes: 1 addition & 1 deletion rdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
#
# or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

MY_VERSION = "0.8.1"
MY_VERSION = "0.8.2"
39 changes: 20 additions & 19 deletions rdk/rdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
"AWS::EKS::Cluster",
"AWS::Elasticsearch::Domain",
"AWS::QLDB::Ledger",
"AWS::Kineses::Stream",
"AWS::Kineses::StreamConsumer",
"AWS::Redshift::Cluster",
"AWS::Redshift::ClusterParameterGroup",
"AWS::Redshift::ClusterSecurityGroup",
Expand Down Expand Up @@ -113,6 +115,10 @@
"AWS::AutoScaling::LaunchConfiguration",
"AWS::AutoScaling::ScalingPolicy",
"AWS::AutoScaling::ScheduledAction",
"AWS::Backup::BackupPlan",
"AWS::Backup::BackupSelection",
"AWS::Backup::BackupVault",
"AWS::Backup::RecoveryPoint",
"AWS::ACM::Certificate",
"AWS::CloudFormation::Stack",
"AWS::CloudTrail::Trail",
Expand All @@ -138,8 +144,6 @@
"AWS::ServiceCatalog::Portfolio",
"AWS::Shield::Protection",
"AWS::ShieldRegional::Protection",
"AWS::Shield::Protection",
"AWS::ShieldRegional::Protection",
"AWS::SSM::ManagedInstanceInventory",
"AWS::SSM::PatchCompliance",
"AWS::SSM::AssociationCompliance",
Expand All @@ -152,10 +156,6 @@
"AWS::WAFRegional::Rule",
"AWS::WAFRegional::WebACL",
"AWS::WAFRegional::RuleGroup",
"AWS::WAFRegional::RateBasedRule",
"AWS::WAFRegional::Rule",
"AWS::WAFRegional::WebACL",
"AWS::WAFRegional::RuleGroup",
"AWS::WAFv2::WebACL",
"AWS::WAFv2::RuleGroup",
"AWS::WAFv2::ManagedRuleSet",
Expand Down Expand Up @@ -268,7 +268,7 @@ def get_rule_parser(is_required, command):
)
parser.add_argument('rulename', metavar='<rulename>', help='Rule name to create/modify')
runtime_group = parser.add_mutually_exclusive_group()
runtime_group.add_argument('-R', '--runtime', required=False, help='Runtime for lambda function', choices=['nodejs4.3', 'java8', 'python3.6', 'python3.6-lib', 'python3.7', 'python3.7-lib', 'python3.8', 'python3.8-lib', 'dotnetcore1.0', 'dotnetcore2.0'], metavar="")
runtime_group.add_argument('-R', '--runtime', required=False, help='Runtime for lambda function', choices=['nodejs4.3', 'java8', 'python3.6', 'python3.6-lib', 'python3.7', 'python3.7-lib', 'python3.8', 'python3.8-lib', 'python3.9', 'python3.9-lib', 'dotnetcore1.0', 'dotnetcore2.0'], metavar="")
runtime_group.add_argument('--source-identifier', required=False, help="[optional] Used only for creating Managed Rules.")
parser.add_argument('-l','--custom-lambda-name', required=False, help='[optional] Provide custom lambda name')
parser.set_defaults(runtime='python3.6-lib')
Expand Down Expand Up @@ -757,16 +757,17 @@ def create(self):
extension_mapping = {
'java8': '.java',
'python3.6': '.py',
'python3.6-managed':'.py',
'python3.6-lib':'.py',
'python3.6-managed': '.py',
'python3.6-lib': '.py',
'python3.7': '.py',
'python3.7-lib':'.py',
'python3.7-lib': '.py',
'python3.8': '.py',
'python3.8-lib':'.py',
'python3.8-lib': '.py',
'python3.9': '.py',
'python3.9-lib': '.py',
'nodejs4.3': '.js',
'dotnetcore1.0': 'cs',
'dotnetcore2.0': 'cs',
'python3.6-managed': '.py',
}
if self.args.runtime not in extension_mapping:
print ("rdk does not support that runtime yet.")
Expand Down Expand Up @@ -796,7 +797,7 @@ def create(self):
shutil.copyfile(src, dst)
f = fileinput.input(files=dst, inplace=True)
for line in f:
if self.args.runtime in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib']:
if self.args.runtime in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib', 'python3.9-lib']:
if self.args.resource_types:
applicable_resource_list = ''
for resource_type in self.args.resource_types.split(','):
Expand Down Expand Up @@ -1416,7 +1417,7 @@ def deploy(self):
}]
layers = []
if 'SourceRuntime' in rule_params:
if rule_params['SourceRuntime'] in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib']:
if rule_params['SourceRuntime'] in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib', 'python3.9-lib']:
if self.args.rdklib_layer_arn:
layers.append(self.args.rdklib_layer_arn)
else:
Expand Down Expand Up @@ -1776,7 +1777,7 @@ def deploy_organization(self):
}]
layers = []
if 'SourceRuntime' in rule_params:
if rule_params['SourceRuntime'] in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib']:
if rule_params['SourceRuntime'] in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib', 'python3.9-lib']:
if self.args.rdklib_layer_arn:
layers.append(self.args.rdklib_layer_arn)
else:
Expand Down Expand Up @@ -1933,7 +1934,7 @@ def export(self):
layers = []
rdk_lib_version = "0"
if 'SourceRuntime' in rule_params:
if rule_params['SourceRuntime'] in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib']:
if rule_params['SourceRuntime'] in ['python3.6-lib', 'python3.7-lib', 'python3.8-lib', 'python3.9-lib']:
if self.args.rdklib_layer_arn:
layers.append(self.args.rdklib_layer_arn)
else:
Expand Down Expand Up @@ -2003,7 +2004,7 @@ def test_local(self):

for rule_name in rule_names:
rule_params, rule_tags = self.__get_rule_parameters(rule_name)
if rule_params['SourceRuntime'] not in ('python3.6', 'python3.6-lib', 'python3.7', 'python3.7-lib', 'python3.8', 'python3.8-lib'):
if rule_params['SourceRuntime'] not in ('python3.6', 'python3.6-lib', 'python3.7', 'python3.7-lib', 'python3.8', 'python3.8-lib', 'python3.9', 'python3.9-lib'):
print ("Skipping " + rule_name + " - Runtime not supported for local testing.")
continue

Expand Down Expand Up @@ -3143,15 +3144,15 @@ def __wait_for_cfn_stack(self, cfn_client, stackname):
def __get_handler(self, rule_name, params):
if 'SourceHandler' in params:
return params['SourceHandler']
if params['SourceRuntime'] in ['python3.6', 'python3.6-lib', 'python3.7', 'python3.7-lib', 'python3.8', 'python3.8-lib', 'nodejs4.3', 'nodejs6.10', 'nodejs8.10']:
if params['SourceRuntime'] in ['python3.6', 'python3.6-lib', 'python3.7', 'python3.7-lib', 'python3.8', 'python3.8-lib', 'python3.9', 'python3.9-lib', 'nodejs4.3', 'nodejs6.10', 'nodejs8.10']:
return (rule_name+'.lambda_handler')
elif params['SourceRuntime'] in ['java8']:
return ('com.rdk.RuleUtil::handler')
elif params['SourceRuntime'] in ['dotnetcore1.0','dotnetcore2.0']:
return ('csharp7.0::Rdk.CustomConfigHandler::FunctionHandler')

def __get_runtime_string(self, params):
if params['SourceRuntime'] in ['python3.6-lib', 'python3.6-managed', 'python3.7-lib', 'python3.8-lib']:
if params['SourceRuntime'] in ['python3.6-lib', 'python3.6-managed', 'python3.7-lib', 'python3.8-lib', 'python3.9-lib']:
runtime = params['SourceRuntime'].split('-')
return runtime[0]

Expand Down
25 changes: 25 additions & 0 deletions rdk/template/runtime/python3.9-lib/rule_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from rdklib import Evaluator, Evaluation, ConfigRule, ComplianceType
<%ApplicableResources1%>
class <%RuleName%>(ConfigRule):
def evaluate_change(self, event, client_factory, configuration_item, valid_rule_parameters):
###############################
# Add your custom logic here. #
###############################

return [Evaluation(ComplianceType.NOT_APPLICABLE)]

#def evaluate_periodic(self, event, client_factory, valid_rule_parameters):
# pass

def evaluate_parameters(self, rule_parameters):
valid_rule_parameters = rule_parameters
return valid_rule_parameters


################################
# DO NOT MODIFY ANYTHING BELOW #
################################
def lambda_handler(event, context):
my_rule = <%RuleName%>()
evaluator = Evaluator(my_rule<%ApplicableResources2%>)
return evaluator.handle(event, context)
49 changes: 49 additions & 0 deletions rdk/template/runtime/python3.9-lib/rule_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import unittest
from unittest.mock import patch, MagicMock
from botocore.exceptions import ClientError
import rdklib
from rdklib import Evaluation, ComplianceType
import rdklibtest

##############
# Parameters #
##############

# Define the default resource to report to Config Rules
RESOURCE_TYPE = 'AWS::::Account'

#############
# Main Code #
#############

MODULE = __import__('<%RuleName%>')
RULE = MODULE.<%RuleName%>()

CLIENT_FACTORY = MagicMock()

#example for mocking S3 API calls
S3_CLIENT_MOCK = MagicMock()

def mock_get_client(client_name, *args, **kwargs):
if client_name == 's3':
return S3_CLIENT_MOCK
raise Exception("Attempting to create an unknown client")

@patch.object(CLIENT_FACTORY, 'build_client', MagicMock(side_effect=mock_get_client))
class ComplianceTest(unittest.TestCase):

rule_parameters = '{"SomeParameterKey":"SomeParameterValue","SomeParameterKey2":"SomeParameterValue2"}'

invoking_event_iam_role_sample = '{"configurationItem":{"relatedEvents":[],"relationships":[],"configuration":{},"tags":{},"configurationItemCaptureTime":"2018-07-02T03:37:52.418Z","awsAccountId":"123456789012","configurationItemStatus":"ResourceDiscovered","resourceType":"AWS::IAM::Role","resourceId":"some-resource-id","resourceName":"some-resource-name","ARN":"some-arn"},"notificationCreationTime":"2018-07-02T23:05:34.445Z","messageType":"ConfigurationItemChangeNotification"}'

def setUp(self):
pass

def test_sample(self):
self.assertTrue(True)

#def test_sample_2(self):
# response = MODULE.lambda_handler(rdklib.build_lambda_configurationchange_event(self.invoking_event_iam_role_sample, self.rule_parameters), {})
# resp_expected = []
# resp_expected.append(rdklib.build_expected_response('NOT_APPLICABLE', 'some-resource-id', 'AWS::IAM::Role'))
# rdklib.assert_successful_evaluation(self, response, resp_expected)
Loading

0 comments on commit e2b09bb

Please sign in to comment.