cloudformation/FIXEngineApplication.yml [1844:2180]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Properties:
      BrokerNodeGroupInfo:
        BrokerAZDistribution: DEFAULT
        ClientSubnets:
          - !Ref 'PrimaryPrivateSubnet'
          - !Ref 'FailoverPrivateSubnet'
        InstanceType: kafka.m5.large
        SecurityGroups:
          - !Ref 'MSKClusterSecurityGroup'
        StorageInfo:
          EBSStorageInfo:
            VolumeSize: 100
      ClusterName: !Ref 'AWS::StackName'
      EncryptionInfo:
        EncryptionAtRest:
          DataVolumeKMSKeyId: !Ref 'KMSKeyARN'
        EncryptionInTransit:
          ClientBroker: TLS_PLAINTEXT
          InCluster: true
      KafkaVersion: 2.2.1
      NumberOfBrokerNodes: 2
    Type: AWS::MSK::Cluster
  MSKClusterInfoRetriever:
    DependsOn:
      - MSKCluster
      - MSKClusterInfoRetrieverLogGroup
    Properties:
      Properties:
        ClusterARN: !Ref 'MSKCluster'
      ServiceToken: !GetAtt 'MSKClusterInfoRetrieverFunction.Arn'
      Type: Custom::GetMSKClusterInfo
      Version: '1.0'
    Type: AWS::CloudFormation::CustomResource
  MSKClusterInfoRetrieverFunction:
    Condition: ''
    DependsOn:
      - MSKClusterInfoRetrieverRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W89
            reason: This function is only invoked during the deployment of this solution,
              potentially PRIOR to the creation of a VPC, and is never used again.
          - id: W92
            reason: This function is only invoked during the deployment of this solution
              and is never used again. Reserving concurrency would therefore be wasteful.
    Properties:
      Code:
        ZipFile: "import json\nimport boto3\nimport logging\nimport cfnresponse\n\
          from botocore.exceptions import ClientError\n\nLOGGER = logging.getLogger()\n\
          LOGGER.setLevel(logging.INFO)\nlogging.getLogger('boto3').setLevel(logging.INFO)\n\
          logging.getLogger('botocore').setLevel(logging.INFO)\n\n'''\n    - ClusterARN\
          \ | str\n'''\ndef handler(event, context):\n\n    LOGGER.info('----- REQUEST\
          \ RECEIVED -----')\n    LOGGER.info(json.dumps(event))\n\n    responseData\
          \ = {}\n    \n    operation = event['ResourceProperties']['Type'].split('Custom::')[1]\n\
          \    properties = event['ResourceProperties']['Properties']\n    \n    if\
          \ operation == 'GetMSKClusterInfo':\n        \n        if event['RequestType']\
          \ in ['Create', 'Update']:\n        \n            msk_client = boto3.client('kafka')\n\
          \        \n            try:\n        \n                nodes = msk_client.list_nodes(\n\
          \                    ClusterArn = properties['ClusterARN']\n           \
          \     )\n        \n                if 'NodeInfoList' not in nodes or len(nodes['NodeInfoList'])\
          \ == 0:\n        \n                    return cfnresponse.send(event, context,\
          \ cfnresponse.FAILED, responseData)\n        \n                else:\n \
          \       \n                    for node in nodes['NodeInfoList']:\n     \
          \   \n                        if node['BrokerNodeInfo']['BrokerId'] == 1:\n\
          \        \n                            responseData['PrimaryEndpoint'] =\
          \ node['BrokerNodeInfo']['Endpoints'][0]\n        \n                   \
          \     else:\n        \n                            responseData['FailoverEndpoint']\
          \ = node['BrokerNodeInfo']['Endpoints'][0]\n        \n                 \
          \   return cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData,\
          \ properties['ClusterARN'])\n        \n            except ClientError as\
          \ e:\n                \n                print(str(e))\n                return\
          \ cfnresponse.send(event, context, cfnresponse.FAILED, responseData)\n \
          \       \n        elif event['RequestType'] in ['Delete']:\n        \n \
          \           return cfnresponse.send(event, context, cfnresponse.SUCCESS,\
          \ responseData)"
      Description: !Join
        - ''
        - - 'MSKClusterInfoRetriever resource for '
          - !Ref 'AWS::StackName'
          - ' stack'
      Handler: index.handler
      KmsKeyArn: !If
        - KMSKeySupplied
        - !Ref 'KMSKeyARN'
        - ''
      Role: !GetAtt 'MSKClusterInfoRetrieverRole.Arn'
      Runtime: python3.7
      Timeout: 15
    Type: AWS::Lambda::Function
  MSKClusterInfoRetrieverLogGroup:
    Condition: ''
    DependsOn:
      - MSKClusterInfoRetrieverFunction
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W84
            reason: These logs do not contain sensitive data.
    Properties:
      LogGroupName: !Join
        - ''
        - - /aws/lambda/
          - !Ref 'MSKClusterInfoRetrieverFunction'
      RetentionInDays: !Ref 'LogRetentionInDays'
    Type: AWS::Logs::LogGroup
  MSKClusterInfoRetrieverRole:
    Condition: ''
    DependsOn:
      - MSKCluster
    Metadata: {}
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - kafka:ListNodes
                Effect: Allow
                Resource:
                  - !Ref 'MSKCluster'
          PolicyName: PrimaryPolicy
    Type: AWS::IAM::Role
  MSKClusterInitializationStarter:
    DependsOn:
      - MSKClusterPrepPolicy
      - MSKInitializationDocument
      - AutomationDocumentStarterLogGroup
      - MSKClusterPrepSecurityGroupEgressToMSK
    Properties:
      Properties:
        AutomationDocumentName: !Ref 'MSKInitializationDocument'
        EligibleRequestTypes:
          - Create
        ExecutionRoleArn: !GetAtt 'MSKClusterPrepExecutionRole.Arn'
      ServiceToken: !GetAtt 'AutomationDocumentStarterFunction.Arn'
      Type: Custom::StartSSMAutomationDocument
      Version: '1.0'
    Type: AWS::CloudFormation::CustomResource
  MSKClusterInitializationWaiter:
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT15M
    DependsOn:
      - MSKClusterPrepPolicy
      - MSKInitializationDocument
      - AutomationDocumentStarterLogGroup
    Type: AWS::CloudFormation::WaitCondition
  MSKClusterMemoryAlarm:
    Condition: CreateAlarms
    DependsOn:
      - MSKCluster
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref 'SNSTopicArn'
      AlarmDescription: Alerts when MSK cluster's memory utilization exceeds 80%
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: Cluster Name
          Value: !Ref 'MSKCluster'
      EvaluationPeriods: 2
      MetricName: MemoryUsed
      Namespace: AWS/Kafka
      OKActions:
        - !Ref 'SNSTopicArn'
      Period: 60
      Statistic: Average
      Threshold: '80'
      TreatMissingData: notBreaching
    Type: AWS::CloudWatch::Alarm
  MSKClusterPrepExecutionRole:
    DependsOn:
      - MSKClusterPrepInstanceRole
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - ssm.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - ec2:RunInstances
                Effect: Allow
                Resource:
                  - !Join
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ::image/*
                  - !Join
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - :subnet/*
                  - !Join
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - :security-group/
                      - !GetAtt 'MSKClusterPrepSecurityGroup.GroupId'
              - Action:
                  - iam:PassRole
                Effect: Allow
                Resource:
                  - !GetAtt 'MSKClusterPrepInstanceRole.Arn'
              - Action:
                  - cloudformation:SignalResource
                Effect: Allow
                Resource:
                  - !Ref 'AWS::StackId'
          PolicyName: primary-policy
    Type: AWS::IAM::Role
  MSKClusterPrepInstanceRole:
    DependsOn:
      - MSKCluster
      - MSKClusterPrepLogGroup
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - kafka:DescribeCluster
                Effect: Allow
                Resource:
                  - !Ref 'MSKCluster'
              - Action:
                  - logs:PutLogEvents
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:DescribeLogGroups
                  - logs:DescribeLogStreams
                Effect: Allow
                Resource:
                  - !Join
                    - ''
                    - - 'arn:aws:logs:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - ':log-group:'
                      - !Ref 'MSKClusterPrepLogGroup'
                  - !Join
                    - ''
                    - - 'arn:aws:logs:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - ':log-group:'
                      - !Ref 'MSKClusterPrepLogGroup'
                      - :log-stream:*
          PolicyName: primary-policy
    Type: AWS::IAM::Role
  MSKClusterPrepInstanceRoleProfile:
    DependsOn:
      - MSKClusterPrepInstanceRole
    Properties:
      Path: /
      Roles:
        - !Ref 'MSKClusterPrepInstanceRole'
    Type: AWS::IAM::InstanceProfile
  MSKClusterPrepLogGroup:
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W84
            reason: These logs do not contain sensitive data.
    Properties:
      LogGroupName: !Join
        - /
        - - /aws/ssm
          - !Ref 'AWS::StackName'
          - MSKPrepAutomation
      RetentionInDays: !Ref 'LogRetentionInDays'
    Type: AWS::Logs::LogGroup
  MSKClusterPrepPolicy:
    DependsOn:
      - AutomationDocumentStarterRole
      - MSKInitializationDocument
      - MSKClusterPrepExecutionRole
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - iam:PassRole
            Effect: Allow
            Resource:
              - !GetAtt 'MSKClusterPrepExecutionRole.Arn'
          - Action:
              - ssm:StartAutomationExecution
            Effect: Allow
            Resource:
              - !Join
                - ''
                - - 'arn:aws:ssm:'
                  - !Ref 'AWS::Region'
                  - ':'
                  - !Ref 'AWS::AccountId'
                  - :automation-definition/
                  - !Ref 'MSKInitializationDocument'
                  - :$DEFAULT
      PolicyName: MSKClusterPrepPolicy
      Roles:
        - !Ref 'AutomationDocumentStarterRole'
    Type: AWS::IAM::Policy
  MSKClusterPrepSecurityGroup:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



cloudformation/FIXEngineVPCApplication.yml [2009:2345]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Properties:
      BrokerNodeGroupInfo:
        BrokerAZDistribution: DEFAULT
        ClientSubnets:
          - !Ref 'PrimaryPrivateSubnet'
          - !Ref 'FailoverPrivateSubnet'
        InstanceType: kafka.m5.large
        SecurityGroups:
          - !Ref 'MSKClusterSecurityGroup'
        StorageInfo:
          EBSStorageInfo:
            VolumeSize: 100
      ClusterName: !Ref 'AWS::StackName'
      EncryptionInfo:
        EncryptionAtRest:
          DataVolumeKMSKeyId: !Ref 'KMSKeyARN'
        EncryptionInTransit:
          ClientBroker: TLS_PLAINTEXT
          InCluster: true
      KafkaVersion: 2.2.1
      NumberOfBrokerNodes: 2
    Type: AWS::MSK::Cluster
  MSKClusterInfoRetriever:
    DependsOn:
      - MSKCluster
      - MSKClusterInfoRetrieverLogGroup
    Properties:
      Properties:
        ClusterARN: !Ref 'MSKCluster'
      ServiceToken: !GetAtt 'MSKClusterInfoRetrieverFunction.Arn'
      Type: Custom::GetMSKClusterInfo
      Version: '1.0'
    Type: AWS::CloudFormation::CustomResource
  MSKClusterInfoRetrieverFunction:
    Condition: ''
    DependsOn:
      - MSKClusterInfoRetrieverRole
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W89
            reason: This function is only invoked during the deployment of this solution,
              potentially PRIOR to the creation of a VPC, and is never used again.
          - id: W92
            reason: This function is only invoked during the deployment of this solution
              and is never used again. Reserving concurrency would therefore be wasteful.
    Properties:
      Code:
        ZipFile: "import json\nimport boto3\nimport logging\nimport cfnresponse\n\
          from botocore.exceptions import ClientError\n\nLOGGER = logging.getLogger()\n\
          LOGGER.setLevel(logging.INFO)\nlogging.getLogger('boto3').setLevel(logging.INFO)\n\
          logging.getLogger('botocore').setLevel(logging.INFO)\n\n'''\n    - ClusterARN\
          \ | str\n'''\ndef handler(event, context):\n\n    LOGGER.info('----- REQUEST\
          \ RECEIVED -----')\n    LOGGER.info(json.dumps(event))\n\n    responseData\
          \ = {}\n    \n    operation = event['ResourceProperties']['Type'].split('Custom::')[1]\n\
          \    properties = event['ResourceProperties']['Properties']\n    \n    if\
          \ operation == 'GetMSKClusterInfo':\n        \n        if event['RequestType']\
          \ in ['Create', 'Update']:\n        \n            msk_client = boto3.client('kafka')\n\
          \        \n            try:\n        \n                nodes = msk_client.list_nodes(\n\
          \                    ClusterArn = properties['ClusterARN']\n           \
          \     )\n        \n                if 'NodeInfoList' not in nodes or len(nodes['NodeInfoList'])\
          \ == 0:\n        \n                    return cfnresponse.send(event, context,\
          \ cfnresponse.FAILED, responseData)\n        \n                else:\n \
          \       \n                    for node in nodes['NodeInfoList']:\n     \
          \   \n                        if node['BrokerNodeInfo']['BrokerId'] == 1:\n\
          \        \n                            responseData['PrimaryEndpoint'] =\
          \ node['BrokerNodeInfo']['Endpoints'][0]\n        \n                   \
          \     else:\n        \n                            responseData['FailoverEndpoint']\
          \ = node['BrokerNodeInfo']['Endpoints'][0]\n        \n                 \
          \   return cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData,\
          \ properties['ClusterARN'])\n        \n            except ClientError as\
          \ e:\n                \n                print(str(e))\n                return\
          \ cfnresponse.send(event, context, cfnresponse.FAILED, responseData)\n \
          \       \n        elif event['RequestType'] in ['Delete']:\n        \n \
          \           return cfnresponse.send(event, context, cfnresponse.SUCCESS,\
          \ responseData)"
      Description: !Join
        - ''
        - - 'MSKClusterInfoRetriever resource for '
          - !Ref 'AWS::StackName'
          - ' stack'
      Handler: index.handler
      KmsKeyArn: !If
        - KMSKeySupplied
        - !Ref 'KMSKeyARN'
        - ''
      Role: !GetAtt 'MSKClusterInfoRetrieverRole.Arn'
      Runtime: python3.7
      Timeout: 15
    Type: AWS::Lambda::Function
  MSKClusterInfoRetrieverLogGroup:
    Condition: ''
    DependsOn:
      - MSKClusterInfoRetrieverFunction
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W84
            reason: These logs do not contain sensitive data.
    Properties:
      LogGroupName: !Join
        - ''
        - - /aws/lambda/
          - !Ref 'MSKClusterInfoRetrieverFunction'
      RetentionInDays: !Ref 'LogRetentionInDays'
    Type: AWS::Logs::LogGroup
  MSKClusterInfoRetrieverRole:
    Condition: ''
    DependsOn:
      - MSKCluster
    Metadata: {}
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - kafka:ListNodes
                Effect: Allow
                Resource:
                  - !Ref 'MSKCluster'
          PolicyName: PrimaryPolicy
    Type: AWS::IAM::Role
  MSKClusterInitializationStarter:
    DependsOn:
      - MSKClusterPrepPolicy
      - MSKInitializationDocument
      - AutomationDocumentStarterLogGroup
      - MSKClusterPrepSecurityGroupEgressToMSK
    Properties:
      Properties:
        AutomationDocumentName: !Ref 'MSKInitializationDocument'
        EligibleRequestTypes:
          - Create
        ExecutionRoleArn: !GetAtt 'MSKClusterPrepExecutionRole.Arn'
      ServiceToken: !GetAtt 'AutomationDocumentStarterFunction.Arn'
      Type: Custom::StartSSMAutomationDocument
      Version: '1.0'
    Type: AWS::CloudFormation::CustomResource
  MSKClusterInitializationWaiter:
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT15M
    DependsOn:
      - MSKClusterPrepPolicy
      - MSKInitializationDocument
      - AutomationDocumentStarterLogGroup
    Type: AWS::CloudFormation::WaitCondition
  MSKClusterMemoryAlarm:
    Condition: CreateAlarms
    DependsOn:
      - MSKCluster
    Properties:
      ActionsEnabled: true
      AlarmActions:
        - !Ref 'SNSTopicArn'
      AlarmDescription: Alerts when MSK cluster's memory utilization exceeds 80%
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: Cluster Name
          Value: !Ref 'MSKCluster'
      EvaluationPeriods: 2
      MetricName: MemoryUsed
      Namespace: AWS/Kafka
      OKActions:
        - !Ref 'SNSTopicArn'
      Period: 60
      Statistic: Average
      Threshold: '80'
      TreatMissingData: notBreaching
    Type: AWS::CloudWatch::Alarm
  MSKClusterPrepExecutionRole:
    DependsOn:
      - MSKClusterPrepInstanceRole
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - ssm.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - ec2:RunInstances
                Effect: Allow
                Resource:
                  - !Join
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ::image/*
                  - !Join
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - :subnet/*
                  - !Join
                    - ''
                    - - 'arn:aws:ec2:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - :security-group/
                      - !GetAtt 'MSKClusterPrepSecurityGroup.GroupId'
              - Action:
                  - iam:PassRole
                Effect: Allow
                Resource:
                  - !GetAtt 'MSKClusterPrepInstanceRole.Arn'
              - Action:
                  - cloudformation:SignalResource
                Effect: Allow
                Resource:
                  - !Ref 'AWS::StackId'
          PolicyName: primary-policy
    Type: AWS::IAM::Role
  MSKClusterPrepInstanceRole:
    DependsOn:
      - MSKCluster
      - MSKClusterPrepLogGroup
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyDocument:
            Statement:
              - Action:
                  - kafka:DescribeCluster
                Effect: Allow
                Resource:
                  - !Ref 'MSKCluster'
              - Action:
                  - logs:PutLogEvents
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:DescribeLogGroups
                  - logs:DescribeLogStreams
                Effect: Allow
                Resource:
                  - !Join
                    - ''
                    - - 'arn:aws:logs:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - ':log-group:'
                      - !Ref 'MSKClusterPrepLogGroup'
                  - !Join
                    - ''
                    - - 'arn:aws:logs:'
                      - !Ref 'AWS::Region'
                      - ':'
                      - !Ref 'AWS::AccountId'
                      - ':log-group:'
                      - !Ref 'MSKClusterPrepLogGroup'
                      - :log-stream:*
          PolicyName: primary-policy
    Type: AWS::IAM::Role
  MSKClusterPrepInstanceRoleProfile:
    DependsOn:
      - MSKClusterPrepInstanceRole
    Properties:
      Path: /
      Roles:
        - !Ref 'MSKClusterPrepInstanceRole'
    Type: AWS::IAM::InstanceProfile
  MSKClusterPrepLogGroup:
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W84
            reason: These logs do not contain sensitive data.
    Properties:
      LogGroupName: !Join
        - /
        - - /aws/ssm
          - !Ref 'AWS::StackName'
          - MSKPrepAutomation
      RetentionInDays: !Ref 'LogRetentionInDays'
    Type: AWS::Logs::LogGroup
  MSKClusterPrepPolicy:
    DependsOn:
      - AutomationDocumentStarterRole
      - MSKInitializationDocument
      - MSKClusterPrepExecutionRole
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - iam:PassRole
            Effect: Allow
            Resource:
              - !GetAtt 'MSKClusterPrepExecutionRole.Arn'
          - Action:
              - ssm:StartAutomationExecution
            Effect: Allow
            Resource:
              - !Join
                - ''
                - - 'arn:aws:ssm:'
                  - !Ref 'AWS::Region'
                  - ':'
                  - !Ref 'AWS::AccountId'
                  - :automation-definition/
                  - !Ref 'MSKInitializationDocument'
                  - :$DEFAULT
      PolicyName: MSKClusterPrepPolicy
      Roles:
        - !Ref 'AutomationDocumentStarterRole'
    Type: AWS::IAM::Policy
  MSKClusterPrepSecurityGroup:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



