Package: cloud-radar
Cloud-RadarWrite unit and functional tests for AWS Cloudformation.Report Bug·Request Feature·GuideTable of ContentsAbout The ProjectBuilt WithGetting StartedPrerequisitesInstallationUsageRoadmapContributingLicenseContactAcknowledgementsAbout The ProjectCloud-Radar is a python module that allows testing of Cloudformation Templates/Stacks using Python.Unit TestingYou can now unit test the logic contained inside your Cloudformation template. Cloud-Radar takes your template, the desired region and some parameters. We render the template into its final state and pass it back to you.You can Test:That Conditionals in your template evaluate to the correct value.Conditional resources were created or not.That resources have the correct properties.That resources are named as expected because of!Sub.You can test all this locally without worrying about AWS Credentials.Functional TestingThis project is a wrapper around Taskcat. Taskcat is a great tool for ensuring your Cloudformation Template can be deployed in multiple AWS Regions. Cloud-Radar enhances Taskcat by making it easier to write more complete functional tests.Here's How:You can interact with the deployed resources directly with tools you already know like boto3.You can control the lifecycle of the stack. This allows testing if resources were retained after the stacks were deleted.You can run tests without hardcoding them in a taskcat config file.This project is new and it's possible not all features or functionality of Taskcat/Cloudformation are supported (seeRoadmap). If you find something missing or have a use case that isn't covered then please let me know =)Built WithTaskcatcfn_tools from cfn-flipGetting StartedCloud-Radar is available as an easy to install pip package.PrerequisitesCloud-Radar requires python >= 3.8InstallationInstall with pip.pipinstallcloud-radarUsageUnit Testing(Click to expand)Using Cloud-Radar starts by importing it into your test file or framework. We will use thisTemplatefor an example shown below. More scenario based examples are currently being built up in theexamples/unitdirectory of this project.frompathlibimportPathfromcloud_radar.cf.unitimportTemplatetemplate_path=Path("tests/templates/log_bucket/log_bucket.yaml")# template_path can be a str or a Path objecttemplate=Template.from_yaml(template_path.resolve())params={"BucketPrefix":"testing","KeepBucket":"TRUE"}# parameters and region are optional arguments.stack=template.create_stack(params,region="us-west-2")stack.no_resource("LogsBucket")bucket=stack.get_resource("RetainLogsBucket")assert"DeletionPolicy"inbucketassertbucket["DeletionPolicy"]=="Retain"bucket_name=bucket.get_property_value("BucketName")assert"us-west-2"inbucket_nameThe AWSpseudo parametersare all class attributes and can be modified before rendering a template.# The value of 'AWS::AccountId' in !Sub "My AccountId is ${AWS::AccountId}" can be changed:Template.AccountId='8675309'Note: Region should only be changed to change the default value. To change the region during testing pass the desired region to render(region='us-west-2')The default values for pseudo parameters:NameDefault ValueAccountId"555555555555"NotificationARNs[]NoValue""Partition"aws"Region"us-east-1"StackId""StackName""URLSuffix"amazonaws.com"Note: Bold variables are not fully impletmented yet see theRoadmapAt the point of creating theTemplateinstance additional configuration is required to be provided if you are using certain approaches to resolving values.If you useFn::ImportValue, a dictionary of key/value pairs is required containing all the keys that your template uses. If an import name is referenced by the template which is not included in this dictionary, an error will be raised.imports = {
  "FakeKey": "FakeValue"
}

template = Template(template_content, imports=imports)If you useDynamic References, a dictionary containing the service and key/value pairs is required containing all the dynamic references that your template uses. If a dynamic reference is included in the template and not contained in the configuration object, an error will be raised.template_content = {
    "Resources": {
        "Foo": {
            "Type": "AWS::IAM::Policy",
            "Properties": {
                "PolicyName": (
                    "mgt-{{resolve:ssm:/account/current/short_name}}-launch-role-pol"
                ),
            },
        },
    },
}

dynamic_references = {
  "ssm": {
    "/account/current/short_name": "dummy"
  }
}

template = Template(template_content, dynamic_references=dynamic_references)A real unit testing example using Pytest can be seenhereFunctional Testing(Click to expand)Using Cloud-Radar starts by importing it into your test file or framework.frompathlibimportPathfromcloud_radar.cf.e2eimportStack# Stack is a context manager that makes sure your stacks are deleted after testing.template_path=Path("tests/templates/log_bucket/log_bucket.yaml")params={"BucketPrefix":"testing","KeepBucket":"False"}regions=['us-west-2']# template_path can be a string or a Path object.# params can be optional if all your template params have default values# regions can be optional, default region is 'us-east-1'withStack(template_path,params,regions)asstacks:# Stacks will be created and returned as a list in the stacks variable.forstackinstacks:# stack will be an instance of Taskcat's Stack class.# It has all the expected properties like parameters, outputs and resourcesprint(f"Testing{stack.name}")bucket_name=""foroutputinstack.outputs:ifoutput.key=="LogsBucketName":bucket_name=output.valuebreakassert"logs"inbucket_nameassertstack.region.nameinbucket_nameprint(f"Created bucket:{bucket_name}")# Once the test is over then all resources will be deleted from your AWS account.You can use taskcattokensin your parameter values.parameters={"BucketPrefix":"taskcat-$[taskcat_random-string]","KeepBucket":"FALSE",}You can skip the context manager. Here is an example forunittestimportunittestfromcloud-radar.cf.e2eimportStackclassTestLogBucket(unittest.TestCase):@classmethoddefsetUpClass(cls):template_path=Path("tests/templates/log_bucket/log_bucket.yaml")cls.test=Stack(template_path)cls.test.create()@classmethoddeftearDownClass(cls):cls.test.delete()deftest_bucket(self):stacks=self.__class__.test.stacksforstackinstacks:# TestAll the properties and methods of astack instance.A real functional testing example using Pytest can be seenhereRoadmapProjectAdd LogoEasier to pick regions for testingUnitAdd full functionality to pseudo variables.Variables likePartition,URLSuffixshould change if the region changes.Variables likeStackNameandStackIdshould have a better default than ""Handle References to resources that shouldn't exist.It's currently possible that a!Refto a Resource stays in the final template even if that resource is later removed because of a conditional.FunctionalAdd the ability to update a stack instance to Taskcat.See theopen issuesfor a list of proposed features (and known issues).ContributingContributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make aregreatly appreciated.This project uses poetry to manage dependencies and pre-commit to run formatting, linting and tests. You will need to have both installed to your system as well as python 3.9.Fork the ProjectSetup environment (poetry install)Setup commit hooks (pre-commit install)Create your Feature Branch (git checkout -b feature/AmazingFeature)Commit your Changes (git commit -m 'Add some AmazingFeature')Push to the Branch (git push origin feature/AmazingFeature)Open a Pull RequestLicenseDistributed under the Apache-2.0 License. SeeLICENSE.txtfor more information.ContactLevi -@shady_cuzAcknowledgementsTaskcatHypermodern PythonBest-README-Template@dhutchison - He was the first contributor to this project and finished the last couple of features to make this project complete. Thank you!