Netskope LogoNetskope Logo
  • Security Services
  • AI Services
  • Networking Services
  • Analytics Services
  • Integrations
  • getting-started.svgGetting Started
    • Support
    • Community
    • Netskope.com
    © 2026 All Rights Reserved. Netskope Inc.
    Home
    Netskope Public Cloud Security
    Implementation guide to set up AWS accounts in Netskope
    Automatically Add New accounts to Netskope

    Automatically Add New accounts to Netskope

    This feature enables you to add new AWS accounts to the Netskope tenant using the AWS Management Console. The setup requires a CFT, add_accounts_cft.yml which calls Netskope’s REST API to create an instance of the AWS account in the tenant. The CFT can be used along with an existing AWS script in your environment such as automatic AWS account creation and resource provisioning to provide you the benefit of automatically setting up your new AWS accounts in the Netskope tenant.

    The CFT contains a script that calls the following REST APIs to create and manage AWS accounts in the Netskope tenant.

    https://<tenant-URL>/api/v1/public_cloud/account?token=<token>&op=create
    https://<tenant-URL>/api/v1/public_cloud/account?token=<token>&op=update
    https://<tenant-URL>./api/v1/public_cloud/account?token=<token>&op=delete
    For more information on REST API endpoints see, Public Cloud API Endpoints for REST API v1.

    This setup requires you to perform the following:

    1. Create the CFT File

    2. Import the CFT to AWS Management Console

    Create the CFT File

    Create a file called add_accounts_cft.yml and copy the following contents into the file.

    Description: CrossAccountRole creation Template
    Parameters:
      APIToken:
        Type: String
        Description: Enter REST API token for the tenant
        NoEcho: true
      TenantUrl:
        Type: String
        Description: Enter the tenant url
      AccountName:
        Type: String
        Description: AWS account alias(if present) else human readable name
        Default: ""
      AdminEmail:
        Type: String
        Description: Enter admin email
      SecurityScan:
        Type: String
        Default: 'true'
        Description: 'Enter ( "true", "false" ), whether security scan is enabled.'
        AllowedValues:
          - true
          - false
      DLPScan:
        Type: String
        Default: 'true'
        Description: 'Enter ( "true", "false" ), whether DLP Scan is enabled.'
        AllowedValues:
          - true
          - false
      MalwareScan:
        Type: String
        Default: 'false'
        Description: 'Enter ( "true", "false" ), whether Malware Scan is enabled'
        AllowedValues:
          - true
          - false
      SecurityScanInterval:
        Type: String
        Default: '60'
        Description: Select security scan interval
        AllowedValues:
          - '30'
          - '60'
          - '120'
          - '360'
          - '1440'
      TrustedAccountID:
        Type: String
        Description: Enter the account ID provided by Netskope.
      ExternalID:
        Type: String
        Description: Enter the external ID provided by Netskope.
    Conditions:
      AccountNameEmpty: !Equals 
        - Ref: AccountName
        - ""
      StorageScanEnabled: !Or
        - !Equals
          - 'true'
          - Ref: DLPScan
        - !Equals
          - 'true'
          - Ref: MalwareScan
      SecurityScanEnabled: !Equals
        - 'true'
        - Ref: SecurityScan
      AnyFeatureEnabled: !Or
        - !Equals
          - 'true'
          - !Ref DLPScan
        - !Equals
          - 'true'
          - !Ref SecurityScan
    Outputs:
      CrossRoleAccountRoleARN:
        Description: The cross-account role that Netskope will use.
        Value: !GetAtt
          - CrossAccountRole
          - Arn
    Resources:
      AutoAddInstance:
        Condition: AnyFeatureEnabled
        Properties:
          ServiceToken: !GetAtt
            - AutoAddInstanceCall
            - Arn
          adminemail: !Ref AdminEmail
          accountname: 
            Fn::If:
            - AccountNameEmpty
            - Ref: AWS::AccountId
            - Ref: AccountName
          apitoken: !Ref APIToken
          securityscan: !Ref SecurityScan
          introspection: !Ref DLPScan
          malware: !Ref MalwareScan
          securityscaninterval: !Ref SecurityScanInterval
          tenanturl: !Ref TenantUrl
          sspolicy:
            Fn::If:
            - StorageScanEnabled
            - Ref: StorageScanPolicy
            - Ref: AWS::NoValue
          cfpolicy:
            Fn::If:
            - StorageScanEnabled
            - Ref: CloudFormationPolicy
            - Ref: AWS::NoValue
          scpolicy:
            Fn::If:
            - SecurityScanEnabled
            - Ref: SecurityScanRolePolicy
            - Ref: AWS::NoValue
        Type: 'Custom::AutoAddInstance'
      AutoAddInstanceCall:
        Condition: AnyFeatureEnabled
        Properties:
          Code:
            ZipFile: |
              import boto3
              import json
              import time
              import ssl
              from urllib.request import Request, urlopen
              def send(event, context, responseStatus, responseData,
              physicalResourceId=None, noEcho=False):
                  responseUrl = event['ResponseURL']
                  responseBody = {}
                  responseBody['Status'] = responseStatus
                  responseBody['Reason'] = 'See the details in CloudWatch Log Stream: ' + context.log_stream_name
                  responseBody['PhysicalResourceId'] = physicalResourceId or context.log_stream_name
                  responseBody['StackId'] = event['StackId']
                  responseBody['RequestId'] = event['RequestId']
                  responseBody['LogicalResourceId'] = event['LogicalResourceId']
                  responseBody['NoEcho'] = noEcho
                  responseBody['Data'] = responseData
                  json_responseBody = json.dumps(responseBody)
                  try:
                      req = Request(responseUrl, method='PUT')
                      req.add_header('Content-Type', 'application/json; charset=utf-8')
                      jsondataasbyte = json_responseBody.encode("utf-8")
                      req.add_header('Content-Length', len(jsondataasbyte))
                      response = urlopen(req, jsondataasbyte)
                  except Exception as e:
                      print("send(..) failed executing requests.put(..): " + str(e))
              def handler(event, context):
                  properties = event.get('ResourceProperties', {})
                  tenant_url = properties['tenanturl']
                  api_token = properties['apitoken']
                  account_id = properties["ServiceToken"].split(':')[4]
                  instance_name = properties['accountname']
                  time.sleep(30)
                  request_body = {
                      "app": "aws",
                      "instance_name": instance_name,
                  }
                  request_url = "https://{}/api/v1/public_cloud/account?token={}&op={}".format(tenant_url, api_token, '{}')
                  if event['RequestType'] == 'Delete':
                      request_url = request_url.format('delete')
                  else:
                      services = []
                      if properties["introspection"] == "true":
                        services.append("introspection")
                      if properties["securityscan"] == "true":
                        services.append("securityscan")
                      if properties["malware"] == "true":
                        services.append("malware")
                      request_body["use_for"] = services
                      admin_email = properties["adminemail"]
                      if 'securityscan' in services:
                          request_body["securityscan_interval"] = properties['securityscaninterval']
                      if 'Create' == event['RequestType']:
                          request_url = request_url.format('create')
                          request_body['accounts'] = [{
                              'account_id': account_id,
                              'account_name': instance_name,
                              'admin_email': admin_email
                          }]
                      elif event['RequestType'] == 'Update':
                          request_body["admin_email"] = admin_email
                          request_url = request_url.format('update')
                  request_body = json.dumps(request_body)
                  count = 3
                  while count:
                      try:
                          req = Request(request_url)
                          req.add_header('Content-Type', 'application/json; charset=utf-8')
                          jsondataasbyte = request_body.encode("utf-8")
                          req.add_header('Content-Length', len(jsondataasbyte))
                          response = urlopen(req, jsondataasbyte, context=ssl._create_unverified_context(), timeout=30)
                          resp = {"data": response.read().decode("utf-8")}
                          print("RESPONSE: {}".format(resp))
                          if "status" in resp["data"]:
                             json_resp  = json.loads(resp["data"])
                             status = json_resp.get("status")
                             if status != "success":
                                failed = True
                                if json_resp.get("errors"):
                                    for err in json_resp.get("errors"):
                                        check_err_str = "instance_name: {} does not exist".format(instance_name)
                                        if check_err_str in err:
                                            failed = False
                                            break
                                if failed:
                                    send(event, context, "FAILED", resp, 'autoaddaccounts')
                                    break
                          print("wait for 2 mins")
                          time.sleep(120)
                          send(event, context, "SUCCESS", resp, 'autoaddaccounts')
                          break
                      except Exception as exc:
                          print("Response exc : {}".format(str(exc)))
                          if count > 0:
                             count -= 1
                          if count == 0:
                             send(event, context, "FAILED", str(exc), 'autoaddaccounts')
          FunctionName: AutoAddInstanceCall
          Handler: index.handler
          Role: !GetAtt
            - NSLambdaAccessRole
            - Arn
          Runtime: python3.8
          Timeout: 240
        Type: 'AWS::Lambda::Function'
      CloudFormationPolicy:
        Condition: StorageScanEnabled
        Properties:
          PolicyDocument:
            Statement:
              - Action:
                  - cloudformation:CreateStack
                  - cloudformation:UpdateStack
                  - cloudformation:DeleteStack
                Condition:
                  ForAllValues:Null:
                    cloudformation:RoleArn: true
                Effect: Allow
                Resource:
                  - arn:aws:cloudformation:*:*:stack/NetskopeStack/*
              - Action:
                  - cloudformation:DescribeStacks
                  - sns:Publish
                  - sns:Unsubscribe
                  - sns:Subscribe
                  - sns:TagResource
                  - sns:ConfirmSubscription
                  - sns:SetTopicAttributes
                  - sns:ListTopics
                  - sns:CreateTopic
                  - sns:DeleteTopic
                  - sns:GetTopicAttributes
                  - events:DescribeRule
                  - events:ListRules
                  - events:PutEvents
                  - events:EnableRule
                  - events:PutRule
                  - events:PutTargets
                  - events:RemoveTargets
                  - events:DeleteRule
                Effect: Allow
                Resource:
                  - arn:aws:cloudformation:*:*:stack/NetskopeStack/*
                  - arn:aws:sns:*:*:CloudWatchEvent*
                  - arn:aws:events:*:*:rule/NetskopeStack*
            Version: '2012-10-17'
          PolicyName: CloudFormationPolicy
          Roles:
            - !Ref CrossAccountRole
        Type: 'AWS::IAM::Policy'
      CrossAccountRole:
        Condition: AnyFeatureEnabled
        Properties:
          AssumeRolePolicyDocument:
            Statement:
              - Action:
                  - 'sts:AssumeRole'
                Condition:
                  StringEquals:
                    'sts:ExternalId': !Ref ExternalID
                Effect: Allow
                Principal:
                  AWS: !Join
                    - ''
                    - - 'arn:aws:iam::'
                      - !Ref TrustedAccountID
                      - ':root'
                Sid: ''
            Version: 2012-10-17
          ManagedPolicyArns:
            - 'arn:aws:iam::aws:policy/SecurityAudit'
          Path: /
          RoleName: Netskope_Role
        Type: 'AWS::IAM::Role'
      NSLambdaAccessRole:
        Condition: AnyFeatureEnabled
        Properties:
          AssumeRolePolicyDocument:
            Statement:
              - Action:
                  - 'sts:AssumeRole'
                Effect: Allow
                Principal:
                  Service:
                    - lambda.amazonaws.com
            Version: 2012-10-17
          Path: /
          Policies:
            - PolicyDocument:
                Statement:
                  - Action:
                      - 'logs:*'
                    Effect: Allow
                    Resource:
                      - 'arn:aws:logs:*:*:*'
                Version: 2012-10-17
              PolicyName: LogAccess
        Type: 'AWS::IAM::Role'
      SecurityScanRolePolicy:
        Condition: SecurityScanEnabled
        Properties:
          PolicyDocument:
            Statement:
              - Action:
                  - s3:ListBucket
                  - ses:ListIdentityPolicies
                  - s3:GetBucketAcl
                  - s3:GetBucketLocation
                  - s3:ListAllMyBuckets
                  - dynamodb:ListTagsOfResource
                  - sqs:ListDeadLetterSourceQueues
                  - sqs:GetQueueUrl
                  - sqs:GetQueueAttributes
                  - lambda:Get*
                  - lambda:List*
                  - cloudwatch:GetMetricStatistics
                  - eks:ListFargateProfiles
                  - ec2:GetEbsEncryptionByDefault
                  - codebuild:BatchGetProjects
                  - codebuild:ListSourceCredentials
                  - wafv2:GetLoggingConfiguration
                  - ses:GetAccount
                  - cloudtrail:ListTrails
                Effect: Allow
                Resource:
                  - '*'
              - Action:
                  - s3:GetObject
                  - s3:GetObjectVersion
                Effect: Allow
                Resource:
                  - arn:aws:s3:::elasticbeanstalk-*
            Version: '2012-10-17'
          PolicyName: netskope-csa
          Roles:
            - !Ref CrossAccountRole
        Type: 'AWS::IAM::Policy'
      StorageScanPolicy:
        Condition: StorageScanEnabled
        Properties:
          PolicyDocument:
            Statement:
              - Action:
                  - s3:ListAllMyBuckets
                  - s3:ListBucket
                  - s3:GetObject
                  - s3:GetObjectAcl
                  - s3:GetBucketLocation
                  - ec2:DescribeRegions
                  - s3:GetObjectTagging
                  - s3:GetBucketTagging
                  - s3:GetEncryptionConfiguration
                  - s3:GetBucketPublicAccessBlock
                  - s3:GetBucketPolicyStatus
                  - s3:GetBucketAcl
                Effect: Allow
                Resource:
                  - '*'
            Version: '2012-10-17'
          PolicyName: StorageScanPolicy
          Roles:
            - !Ref CrossAccountRole
        Type: 'AWS::IAM::Policy'

    Import the CFT to AWS Management Console

    Import add_accounts_cft.yml to a new CloudFormation stack in each AWS account. To import the CFT,

    1. Log in to the AWS Management Console using the credentials of the AWS account you are setting up with Netskope for IaaS and navigate to Services > CloudFormation.

    2. In the CloudFormation page, click Create stack.

    3. Select Upload a template file and click Choose file to upload the add_accounts_cft.yml . Click Next.

      Auto Add Cft Aws1.png
    4. In the Specify stack details page, specify a Stack name. The stack name must:

      • Only contain alphanumeric characters and hyphens,

      • start with an alphabet, and

      • not be longer than 128 characters.

    5. Define the parameters in the template and click Next. For information, see Provide Input Parameters in the Stack Details Page.

      Auto Add Cft Aws2.png
    6. In the Configure stack options page, use the default configuration, and click Next.

    7. Review your stack details on the Review page, click the acknowledgment and then click Create stack.

    When the creation process is complete, your stack will be displayed on the CloudFormation page and the AWS account will be setup in the Netskope tenant with the services specified in the add_accounts_cft.yml.

    Provide Input Parameters in the Stack Details Page

    You must provide the following input parameters in the stack created using add_accounts_cft.yml to set up the AWS account with Netskope’s Public Cloud Security features such a Continuous Security Assessment and Storage Scan.

    ParameterValue
    APITokenEnter Netskope’s REST API token.
    AccountNameEnter an AWS account name, preferably the AWS account alias.
    AdminEmailThis parameter is optional. Enter an admin email.
    DLPScanSet to true by default to enable DLP scans. To disable DLP scans, enter false.
    ExternalIDFollow the steps below to find the external ID.

    Note

    These are dummy steps to identify the external ID only.

    1. Navigate to Settings > Configure App Access > Classic > IaaS, then select AWS and click Setup.
    2. In the New Setup window, enter the following details:
      1. 12 digit AWS account ID, followed by the account name. Follow the format as described in the text box.
      2. Keep the default service checked and click Next.
    3. Download the CFT file.
    4. Exit the New Setup window.
    5. Open the CFT file and find the ExternalId.
    MalwareScanSet to true by default to enable threat and malware scans. To disable threat and malware scans, enter false.
    SecurityScanSet to true by default to enable security assessment scans. To disable security assessment scans, enter false.
    SecurityScanIntervalThe default scan interval is set to 60 minutes. You can change the interval time in minutes to one of the following values.
    • 30
    • 120
    • 360
    • 1440
    TenantUrlEnter Netskope’s tenant URL.
    TrustedAccountIDFollow the steps below to find the trusted account ID.

    Note

    These are dummy steps to identify the trusted account ID only.

    1. Log in to the Netskope tenant UI.
    2. Navigate to Settings > Configure App Access > Classic > IaaS, then select AWS and click Setup.
    3. In the New Setup window, enter the following details:
      1. 12 digit AWS account ID, followed by the account name. Follow the format as described in the text box.
      2. Keep the default service checked and click Next.
    4. Download the CFT file.
    5. Exit the New Setup window.
    6. Open the CFT file and find AWS: – arn:aws:iam::<TrustedAccountID>:root.
    In this Topic
    • Automatically Add New accounts to Netskope