diff --git a/Common/Cloud-Account/aws-cfn-cloud-account-connector/README.md b/Common/Cloud-Account/aws-cfn-cloud-account-connector/README.md index b2a538bd..a569e742 100644 --- a/Common/Cloud-Account/aws-cfn-cloud-account-connector/README.md +++ b/Common/Cloud-Account/aws-cfn-cloud-account-connector/README.md @@ -1,6 +1,6 @@ # Add AWS Account to Cloud One -To fully integrate an AWS account in Cloud One, you must deploy resources in your AWS account and do manual steps in Trend Cloud One dashboard. This CloudFormation template automates all these steps on your behalf, including integrating it to Vision One. +To fully integrate an AWS account in Cloud One, you must deploy resources in your AWS account and do manual steps in Trend Cloud One dashboard. This CloudFormation template automates all these steps on your behalf, including integrating it to Vision One. You can also optionally also deploy Sentry and Network Security with hosted infrastructure to these AWS accounts via this automation. ## What does it actually do? @@ -47,6 +47,10 @@ To fully integrate an AWS account in Cloud One, you must deploy resources in you - Description: Decides if a new Trail should be created. Defaults to False, so you must enter a S3 Bucket name in the ExistingCloudtrailBucketName parameter. In case you pick True, a new trail and bucket will be created for you. Setting this to True will incur in extra costs. - ExistingCloudtrailBucketName: - Description: Specify the name of an existing bucket that you want to use for forwarding to Trend Micro Cloud One. Only used if CreateNewTrail is set to False. +- DeployCloudSentry: + - Description: Decides if the Cloud Sentry integration should be deployed. Defaults to True. +- DeployNetworkSecurityIntegration: + - Description: Decides if the Network Security integration should be deployed. Defaults to True. ### Shouldn't be Changed from Default diff --git a/Common/Cloud-Account/aws-cfn-cloud-account-connector/cloudone.template.yaml b/Common/Cloud-Account/aws-cfn-cloud-account-connector/cloudone.template.yaml index e8289808..0a82d2be 100644 --- a/Common/Cloud-Account/aws-cfn-cloud-account-connector/cloudone.template.yaml +++ b/Common/Cloud-Account/aws-cfn-cloud-account-connector/cloudone.template.yaml @@ -23,7 +23,17 @@ Parameters: - jp-1 - ca-1 - de-1 - + CloudOneFeatures: + Description: Comma separated list of Cloud One Features to deploy. Defaults to deploy Sentry. + More info at https://cloudone.trendmicro.com/docs/cloud-account-management/api-reference/tag/AWS#operation/describeAWSStackTemplate + Type: String + Default: cloud-sentry + RegionsToDeploy: + Description: Comma separated list of AWS Regions where the Features will be deployed to. If empty, all accessible regions will be used. + More info at https://cloudone.trendmicro.com/docs/cloud-account-management/api-reference/tag/AWS#operation/describeAWSStackTemplate + Type: String + Default: "" + Resources: CloudOneIntegrationStack: @@ -33,7 +43,7 @@ Resources: CloudOneRegion: !Ref CloudOneRegion CloudOneAccountID: !Ref CloudOneAccountID CloudOneOIDCProviderURL: !Sub 'cloudaccounts.${CloudOneRegion}.cloudone.trendmicro.com' - TemplateURL: !Sub 'https://cloud-one-cloud-accounts-${AWS::Region}.s3.${AWS::Region}.amazonaws.com/templates/aws/cloud-account-management-role.template' + TemplateURL: !GetAtt GetCloudOneIntegrationTemplate.templateURL AddAWSAccountToCloudOneFunction: Type: AWS::Lambda::Function @@ -51,63 +61,61 @@ Resources: CloudOneApiKey: !Ref CloudOneApiKey Code: ZipFile: - !Sub - |- - import json - import os - import urllib3 - import boto3 - import cfnresponse - - def lambda_handler(event, context): - status = cfnresponse.SUCCESS - response_data = {} - physicalResourceId = None - try: - - cloudOneRoleArn = os.environ['CloudOneRoleArn'] - cloudOneRegion = os.environ['CloudOneRegion'] - cloudOneApiKey = os.environ['CloudOneApiKey'] - - headers = { - 'api-version': 'v1', - 'Authorization': 'ApiKey '+cloudOneApiKey+'', - 'Content-Type': 'application/json' - } + |- + import json + import os + import urllib3 + import cfnresponse + + def lambda_handler(event, context): + status = cfnresponse.SUCCESS + response_data = {} + physicalResourceId = None + try: + + cloudOneRoleArn = os.environ['CloudOneRoleArn'] + cloudOneRegion = os.environ['CloudOneRegion'] + cloudOneApiKey = os.environ['CloudOneApiKey'] + + headers = { + 'api-version': 'v1', + 'Authorization': 'ApiKey '+cloudOneApiKey+'', + 'Content-Type': 'application/json' + } - http = urllib3.PoolManager() + http = urllib3.PoolManager() + + + if event["RequestType"] == "Create" or event["RequestType"] == "Update": + url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws' + + payload = json.dumps({ + 'roleARN': cloudOneRoleArn + }) + encoded_payload = payload.encode("utf-8") + print(url) + response = http.request("POST", url=url, headers=headers, body=encoded_payload) + print(response) + response_json_data = json.loads(response.data.decode("utf-8")) + print(response_json_data) + physicalResourceId = response_json_data["id"] + response_data = {"ID": response_json_data["id"]} + + else: # if event["RequestType"] == "Delete": + id = event["PhysicalResourceId"] + + url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/' + id + + print(url) + response = http.request("DELETE", url=url, headers=headers) + print(response) - if event["RequestType"] == "Create" or event["RequestType"] == "Update": - - url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws' - - payload = json.dumps({ - 'roleARN': cloudOneRoleArn - }) - encoded_payload = payload.encode("utf-8") - print(url) - response = http.request("POST", url=url, headers=headers, body=encoded_payload) - print(response) - response_json_data = json.loads(response.data.decode("utf-8")) - print(response_json_data) - physicalResourceId = response_json_data["id"] - response_data = {"ID": response_json_data["id"]} - - else: # if event["RequestType"] == "Delete": - id = event["PhysicalResourceId"] - - url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/' + id - - print(url) - response = http.request("DELETE", url=url, headers=headers) - print(response) - - except Exception as e: - print(e) - status = cfnresponse.FAILED - - cfnresponse.send(event, context, status, response_data, physicalResourceId) + except Exception as e: + print(e) + status = cfnresponse.FAILED + + cfnresponse.send(event, context, status, response_data, physicalResourceId) AddAWSAccountToCloudOne: Type: AWS::CloudFormation::CustomResource @@ -128,9 +136,124 @@ Resources: - sts:AssumeRole Path: "/" ManagedPolicyArns: - - Fn::Join: - - "" - - - "arn:" - - Ref: AWS::Partition - - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + + GetCloudOneIntegrationTemplateFunction: + Type: AWS::Lambda::Function + Properties: + Runtime: python3.9 + Architectures: + - arm64 + Timeout: 60 + Handler: index.lambda_handler + Role: !GetAtt GetCloudOneIntegrationTemplateFunctionRole.Arn + Environment: + Variables: + CloudOneRegion: !Ref CloudOneRegion + CloudOneApiKey: !Ref CloudOneApiKey + AWSRegion: !Ref AWS::Region + CloudOneFeatures: !Ref CloudOneFeatures + RegionsToDeploy: !Ref RegionsToDeploy + Code: + ZipFile: + |- + import json + import os + import urllib3 + import boto3 + import cfnresponse + + ec2 = boto3.client('ec2') + + def lambda_handler(event, context): + status = cfnresponse.SUCCESS + response_data = {} + physicalResourceId = None + try: + + cloudOneRegion = os.environ['CloudOneRegion'] + cloudOneApiKey = os.environ['CloudOneApiKey'] + aws_region = os.environ['AWSRegion'] or os.environ['AWS_REGION'] + features = os.environ['CloudOneFeatures'] + regions_to_deploy = os.environ['RegionsToDeploy'] + + headers = { + 'api-version': 'v1', + 'Authorization': 'ApiKey '+cloudOneApiKey+'', + 'Content-Type': 'application/json' + } + + http = urllib3.PoolManager() + + if event["RequestType"] == "Create" or event["RequestType"] == "Update": + + if not regions_to_deploy: + response = [region['RegionName'] for region in ec2.describe_regions()['Regions']] + print('Regions:', response) + regions_to_deploy = ",".join(response) + + url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/templates' + + query_parameters = { + "features": features, + "awsRegion": aws_region, + "featureAWSRegions": regions_to_deploy + } + + print(url) + response = http.request("GET", url=url, headers=headers, fields=query_parameters) + print(response) + response_json_data = json.loads(response.data.decode("utf-8")) + print(response_json_data) + physicalResourceId = response_json_data["templateURL"] + response_data = { + "templateURL": response_json_data["templateURL"], + "parameters": response_json_data["parameters"] + } + + else: # if event["RequestType"] == "Delete": + id = event["PhysicalResourceId"] + + url = 'https://cloudaccounts.'+cloudOneRegion+'.cloudone.trendmicro.com/api/cloudaccounts/aws/' + id + + print(url) + response = http.request("DELETE", url=url, headers=headers) + print(response) + + except Exception as e: + print(e) + status = cfnresponse.FAILED + + cfnresponse.send(event, context, status, response_data, physicalResourceId) + + GetCloudOneIntegrationTemplate: + Type: AWS::CloudFormation::CustomResource + Properties: + ServiceToken: !GetAtt GetCloudOneIntegrationTemplateFunction.Arn + + GetCloudOneIntegrationTemplateFunctionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - sts:AssumeRole + Path: "/" + ManagedPolicyArns: + - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + Policies: + - PolicyName: GetAvailableRegions + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - ec2:DescribeRegions + Resource: '*' + diff --git a/Common/Cloud-Account/aws-cfn-cloud-account-connector/main.template.yaml b/Common/Cloud-Account/aws-cfn-cloud-account-connector/main.template.yaml index c9b6442c..656d1b77 100644 --- a/Common/Cloud-Account/aws-cfn-cloud-account-connector/main.template.yaml +++ b/Common/Cloud-Account/aws-cfn-cloud-account-connector/main.template.yaml @@ -11,6 +11,8 @@ Metadata: - VisionOneServiceToken - CreateNewTrail - ExistingCloudtrailBucketName + - DeployCloudSentry + - DeployNetworkSecurityIntegration - Label: default: 'Warning: Do not modify the fields below unless you know what you are doing. Modifications may cause your deployment to fail.' @@ -23,12 +25,32 @@ Metadata: VisionOneServiceToken: default: VisionOneServiceToken - Parameters: CloudOneApiKey: Description: Cloud One API Key. You can learn more about it at https://cloudone.trendmicro.com/docs/identity-and-account-management/c1-api-key/ Type: String NoEcho: true + # DeployCloudTrailIntegration: + # Description: Decides if the CloudTrail integration should be deployed. Defaults to True. + # Type: String + # AllowedValues: + # - "True" + # - "False" + # Default: "True" + DeployCloudSentry: + Description: Decides if the Cloud Sentry integration should be deployed. Defaults to True. + Type: String + AllowedValues: + - "True" + - "False" + Default: "True" + DeployNetworkSecurityIntegration: + Description: Decides if the Network Security integration should be deployed. Defaults to True. + Type: String + AllowedValues: + - "True" + - "False" + Default: "True" VisionOneServiceToken: Description: Vision One Service Token. See step 1 at https://docs.trendmicro.com/en-us/enterprise/trend-micro-xdr-help/ConfiguringCloudOneWorkloadSecurity/ Type: String @@ -72,6 +94,17 @@ Conditions: HasNoExistingCloudtrailBucketName: !Equals ["True", !Ref CreateNewTrail] + DeployCloudSentry: + !Equals [!Ref DeployCloudSentry, "True"] + + DeployNetworkSecurityIntegration: + !Equals [!Ref DeployNetworkSecurityIntegration, "True"] + + DeployCloudSentryAndNetworkSecurityIntegration: + !And + - !Equals [!Ref DeployCloudSentry, "True"] + - !Equals [!Ref DeployNetworkSecurityIntegration, "True"] + Resources: GetCloudOneRegionAndAccountStack: Type: AWS::CloudFormation::Stack @@ -106,6 +139,7 @@ Resources: CloudOneRegion: !GetAtt GetCloudOneRegionAndAccountStack.Outputs.CloudOneRegion CloudOneAccountID: !GetAtt GetCloudOneRegionAndAccountStack.Outputs.CloudOneAccountId CloudOneApiKey: !Ref CloudOneApiKey + CloudOneFeatures: !If [DeployCloudSentryAndNetworkSecurityIntegration, "cloud-sentry,network-security-deployment", !If [DeployCloudSentry, "cloud-sentry", !If [DeployNetworkSecurityIntegration, "network-security-deployment", ""]]] TemplateURL: !Sub 'https://${QSS3BucketName}.s3.amazonaws.com/${QSS3KeyPrefix}Common/Cloud-Account/aws-cfn-cloud-account-connector/cloudone.template.yaml' DependsOn: - VisionOneEnrollmentStack