{
   "AWSTemplateFormatVersion":"2010-09-09",
   "Description":" This AWS CloudFormation template creates resources for performing continuous golden AMI vulnerability assessment.",
   "Resources":{
      "StartContinuousAssessmentLambdaRole":{
         "Type":"AWS::IAM::Role",
         "Properties":{
            "ManagedPolicyArns":[
               "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
               "arn:aws:iam::aws:policy/AmazonInspectorFullAccess"
            ],
            "AssumeRolePolicyDocument":{
               "Version":"2012-10-17",
               "Statement":[
                  {
                     "Effect":"Allow",
                     "Principal":{
                        "Service":[
                           "lambda.amazonaws.com"
                        ]
                     },
                     "Action":[
                        "sts:AssumeRole"
                     ]
                  }
               ]
            },
            "Path":"/",
            "Policies":[
               {
                  "PolicyName":"StartContinuousAssessmentLambdaPolicy",
                  "PolicyDocument":{
                     "Version":"2012-10-17",
                     "Statement":[{  
                     "Sid": "StartContinuousAssessmentLambdaPolicyStmt",
                     "Effect": "Allow",
                     "Action":["ssm:GetParameter","ec2:DescribeImages", "ec2:RunInstances","ec2:CreateTags"],
                     "Resource": ["*"]
                     }]
                  }
               }
            ]
         }
      },
      "StartContinuousAssessmentLambdaFunction":{
         "Type":"AWS::Lambda::Function",
         "Properties":{
            "Role":{
               "Fn::GetAtt":[
                  "StartContinuousAssessmentLambdaRole",
                  "Arn"
               ]
            },
            "Code":{
               "ZipFile":{
                  "Fn::Join":[
                     "",
                     [
                  "\n","import json",
                  "\n","import urllib.parse",
                  "\n","import boto3",
                  "\n","import time",
                  "\n","import os", 
                  "\n","def lambda_handler(event, context):",
                  "\n","    AMIsParamName = event['AMIsParamName'];",
                  "\n","    region=os.environ['AWS_DEFAULT_REGION']",   
                  "\n","    ec2 = boto3.client('ec2',region)", 
                  "\n","    ssm = boto3.client('ssm',region)",
                  "\n","    inspector = boto3.client('inspector',region)",                    
                  "\n","    AmiJson =  ssm.get_parameter(Name=AMIsParamName)['Parameter']['Value']",
                  "\n","    items = json.loads(AmiJson)",                   
                  "\n","    for entry in items:",
                  "\n","        images= ec2.describe_images(ImageIds=[entry['ami-id']],DryRun=False)",
                  "\n","        tags = images['Images'][0]['Tags']",
                  "\n","        tags.append({'Key': 'continuous-assessment-instance', 'Value': 'true'})",
                  "\n","        ec2.run_instances(ImageId=entry['ami-id'],InstanceType=entry['instanceType'],UserData=entry['userData'],DryRun=False,MaxCount=1,MinCount=1,TagSpecifications=[{'ResourceType': 'instance','Tags': tags}])",
                  "\n","    assessmentTemplateArn='';", 
                  "\n","    rules = inspector.list_rules_packages();",
                  "\n","    ",
                  "\n","    millis = int(round(time.time() * 1000))",
                  "\n","    existingTemplates = inspector.list_assessment_templates(filter={'namePattern': 'ContinuousAssessment'})",
                  "\n","    print('Total templates found:'+str(len(existingTemplates['assessmentTemplateArns'])))",                      
                  "\n","    if len(existingTemplates['assessmentTemplateArns'])==0:",
                  "\n","        resGroup = inspector.create_resource_group(resourceGroupTags=[{'key': 'continuous-assessment-instance','value': 'true'}])",
                  "\n","        target = inspector.create_assessment_target(assessmentTargetName='ContinuousAssessment',resourceGroupArn=resGroup['resourceGroupArn'])",
                  "\n","        template = inspector.create_assessment_template(assessmentTargetArn=target['assessmentTargetArn'],assessmentTemplateName='ContinuousAssessment', durationInSeconds=3600,rulesPackageArns=rules['rulesPackageArns'])",
                  "\n","        assessmentTemplateArn=template['assessmentTemplateArn']",
                  "\n","        response = inspector.subscribe_to_event(event='ASSESSMENT_RUN_COMPLETED',resourceArn=template['assessmentTemplateArn'],topicArn='",{"Ref":"ContinuousAssessmentCompleteTopic"},"') ",
                  "\n","        print('Template Created:'+template['assessmentTemplateArn'])",                   
                  "\n","    else:",
                  "\n","         assessmentTemplateArn=existingTemplates.get('assessmentTemplateArns')[0]",
                  "\n","    time.sleep(240)",
                  "\n","    run = inspector.start_assessment_run(assessmentTemplateArn=assessmentTemplateArn,assessmentRunName='ContinuousAssessment'+'-'+str(millis))",
                  "\n","    return 'Done'"  
 ]
                  ]
               }
            },
            "Runtime":"python3.6", 
            "Timeout":300,
            "Handler":"index.lambda_handler",
            "MemorySize":512
         }
      },  
      "ContinuousAssessmentResultsTopic":{
         "Type":"AWS::SNS::Topic"
      }, 
      "ContinuousAssessmentCompleteTopicSubscription":{
         "Type":"AWS::SNS::Subscription",
         "Properties": {
            "Endpoint":{
               "Fn::GetAtt":[
                  "AnalyzeInspectorFindingsLambdaFunction",
                  "Arn"
               ]
            },
            "Protocol":"lambda",
            "TopicArn": { "Ref":"ContinuousAssessmentCompleteTopic"}
         }
      },
      "ContinuousAssessmentCompleteTopic":{
         "Type":"AWS::SNS::Topic"
      },
      "ContinuousAssessmentCompleteTopicPolicy":{
         "Type":"AWS::SNS::TopicPolicy",
         "Properties":{
            "PolicyDocument":{
               "Id":"MyTopicPolicy",
               "Version":"2012-10-17",
               "Statement":[
                  {
                     "Sid":"My-statement-id",
                     "Effect":"Allow",
                     "Principal":{
                        "Service": "inspector.amazonaws.com"
                     },
                     "Action":"sns:Publish",
                     "Resource":"*"
                  }
               ]
            },
            "Topics":[
               {
                  "Ref":"ContinuousAssessmentCompleteTopic"
               }
            ]
         }
      },  
      "LambdaInvokePermission":{
         "Type":"AWS::Lambda::Permission",
         "Properties":{
            "Action":"lambda:InvokeFunction",
            "Principal":"sns.amazonaws.com",
            "SourceArn":{
               "Ref":"ContinuousAssessmentCompleteTopic"
            },
            "FunctionName":{
               "Fn::GetAtt":[
                  "AnalyzeInspectorFindingsLambdaFunction",
                  "Arn"
               ]
            }
         }
      },
     
       "AnalyzeInspectorFindingsLambdaRole":{
         "Type":"AWS::IAM::Role",
         "Properties":{
            "ManagedPolicyArns":[
               "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
            ],
            "AssumeRolePolicyDocument":{
               "Version":"2012-10-17",
               "Statement":[
                  {
                     "Effect":"Allow",
                     "Principal":{
                        "Service":[
                           "lambda.amazonaws.com"
                        ]
                     },
                     "Action":[
                        "sts:AssumeRole"
                     ]
                  }
               ]
            },
            "Path":"/",
            "Policies":[
               {
                  "PolicyName":"AnalyzeInspectorFindingsLambdaPolicy",
                  "PolicyDocument":{
                     "Version":"2012-10-17",
                     "Statement":[{  
                     "Sid": "AnalyzeInspectorFindingsLambdaPolicyStmt",
                     "Effect": "Allow",
                     "Action":["sns:Publish","ec2:DescribeInstances","ec2:TerminateInstances","inspector:AddAttributesToFindings","inspector:DescribeFindings","inspector:ListFindings"],
                     "Resource": ["*"]
                     }]
                  }
               }
            ]
         }
      }, 
       "AnalyzeInspectorFindingsLambdaFunction":{
         "Type":"AWS::Lambda::Function",
         "Properties":{
            "Role":{
               "Fn::GetAtt":[
                  "AnalyzeInspectorFindingsLambdaRole",
                  "Arn"
               ]
            },
            "Code":{
               "ZipFile":{
                  "Fn::Join":[
                     "",
                     [ 
                        "\n","import json ",
                        "\n","import os",
                        "\n","import boto3",
                        "\n","import collections",
                        "\n","import ast",
                        "\n","def lambda_handler(event, context): ",
                        "\n","    message = event['Records'][0]['Sns']['Message'] ",
                        "\n","    jsonVal = json.loads(message);",
                        "\n","    assessmentArn =jsonVal['run']  ",
                        "\n","    region=os.environ['AWS_DEFAULT_REGION']",     
                        "\n","    ec2 = boto3.client('ec2',region) ",
                        "\n","    sns = boto3.client('sns',region) ",
                        "\n","    inspector = boto3.client('inspector',region) ",   
                        "\n","    findingArns = inspector.list_findings(assessmentRunArns=[jsonVal['run']],maxResults=5000)",
                        "\n","    aggregateData={}",
                        "\n","    for findingArn in findingArns['findingArns']:",
                        "\n","        finding = inspector.describe_findings(findingArns=[findingArn]) ",
                        "\n","        for result in finding['findings']: ",
                        "\n","            instanceId =result['assetAttributes']['agentId']",
                        "\n","            severity =result['severity']",
                        "\n","            cveName=result['id']",
                        "\n","            if not (instanceId) in aggregateData:",
                        "\n","                aggregateData[instanceId]={}",
                        "\n","                aggregateData[instanceId]['findings']={}",
                        "\n","                aggregateData[instanceId]['findings'][severity]=0",
                        "\n","                instance=ec2.describe_instances(InstanceIds=[instanceId]);",
                        "\n","                tagsStr=str(instance['Reservations'][0]['Instances'][0]['Tags']) ",
                        "\n","                tagsStr =tagsStr.replace('Key','key').replace('Value','value')  ",
                        "\n","                aggregateData[instanceId]['tags']= ast.literal_eval(tagsStr)",
                        "\n","            elif not (severity) in aggregateData[instanceId]['findings']:",
                        "\n","                aggregateData[instanceId]['findings'][severity]=0",
                        "\n","            aggregateData[instanceId]['findings'][severity]=aggregateData[instanceId]['findings'][severity]+1; ",
                        "\n","            inspector.add_attributes_to_findings(findingArns=[result['arn']],attributes=aggregateData[instanceId]['tags'])",
                        "\n","    tagsList=[]",  
                        "\n","    for key  in aggregateData: ",
                        "\n","        outputJson=[] ",
                        "\n","        for tag in aggregateData[key]['tags']:",
                        "\n","            if tag['key'] != 'continuous-assessment-instance':",
                        "\n","                outputJson.append(\"\\\"\"+tag['key']+\"\\\"\"+\":\"+\"\\\"\"+tag['value']+\"\\\"\")",
                        "\n","        for sev in aggregateData[key]['findings']:",
                        "\n","            outputJson.append(\"\\\"Finding-Severity-\"+sev+\"-Count\\\"\"+\":\"+\"\\\"\"+str(aggregateData[key]['findings'][sev])+\"\\\"\")",
                        "\n","        outputJson.sort()",
                        "\n","        print(outputJson)",
                        "\n","        tagsList.append('{'+', '.join(outputJson)+'}')",
                        "\n","        print('Terminating:'+key)",
                        "\n","        ec2.terminate_instances(InstanceIds=[key],DryRun=False)",
                        "\n","    sns.publish(TopicArn='", {"Ref":"ContinuousAssessmentResultsTopic"}, "',Message='['+', '.join(tagsList)+']')",
                        "\n","    return jsonVal['run']",
                     ]
                  ]
               }
            },
            "Runtime":"python3.6", 
            "Timeout":300,
            "Handler":"index.lambda_handler",
            "MemorySize":512
         }
      }
   },
   "Outputs":{
      "StartContinuousAssessmentLambdaFunction":{
         "Description":"The Lambda function that initiates the vulnerability assessment.",
         "Value":{
            "Ref":"StartContinuousAssessmentLambdaFunction"
         }
      },
      "ContinuousAssessmentResultsTopic":{
         "Description":"The SNS topic on which consolidated results are published",
         "Value":{
            "Ref":"ContinuousAssessmentResultsTopic"
         }
      }
   }
}