1 - Integrating Cloud Protect with PPC (Protegrity Provisioned Cluster)

Concepts for integrating with PPC (Protegrity Provisioned Cluster)

    This guide describes how to configure the Protegrity Policy Agent and Log Forwarder to connect to a Protegrity Provisioned Cluster (PPC), highlighting the differences from connecting to ESA.

    Key Differences: PPC vs ESA

    FeatureESA 10.2PPC (this guide)
    Datastore Key FingerprintOptional/RecommendedRequired
    CA Certificate on AgentOptional/RecommendedOptional/Recommended
    CA Certificate on Log ForwarderOptional/RecommendedNot supported
    Client Certificate Authentication from Log ForwarderOptional/RecommendedNot supported
    IP AddressESA IP addressPPC address

    Prerequisites

    • Access to PPC and required credentials.
    • Tools: curl, kubectl installed.

    Policy Agent Setup with PPC

    Follow these instructions as a guide for understanding specific inputs for Policy Agent integrating with PPC:

    1. Obtain the Datastore Key Fingerprint

      To retrieve the fingerprint for your Policy Agent:

      1. Retrieve public key from the Cloud Provider Key Management service for the policy encryption key created in pre-configuration:

        1. Navigate to the Key Management Service in AWS console and open Customer Managed Keys
        2. Select the desired key
        3. Select the Public Key tab
        4. Select Download
        1. Navigate to the Key Vault in Azure console and open Objects>Keys
        2. Select the desired key
        3. Select the key indicated as CURRENT VERSION
        4. Select Download public key
        1. Navigate to Key Management in GCP console
        2. Select the desired key and open the Versions tab
        3. Select Get public key from the Actions column menu
        4. Select Download

      2. Escape the new line characters in the downloaded public key for use in the next step - for example:

        awk 'NF {printf "%s\\n",$0}' "<public_key_file>" > "new-line-escaped-public-key.pem"
        cat new-line-escaped-public-key.pem
        
      3. Export key fingerprint using the PPC API as indicated in the curl example below:

        curl -k -H "Authorization: Bearer ${TOKEN}" -X POST https://${HOST}/pty/v2/pim/datastores/1/export/keys  -H "Content-Type: application/json" --data '{
          "algorithm": "RSA-OAEP-256",
          "description": "example-key-from-key-management",
          "pem": "<value of new-line-escaped-public-key>"
        }'
        

        Sample Output:

        {"uid":"1","algorithm":"RSA-OAEP-256","fingerprint":"4c:46:d8:05:35:2e:eb:39:4d:39:8e:6f:28:c3:ab:d3:bc:9e:7a:cb:95:cb:b1:8e:b5:90:21:0f:d3:2c:0b:27","description":"example-key-from-kms"}
        
      4. Record the value for fingerprint and configure the Policy Agent:

        Set the environment variable PTY_DATASTORE_KEY in the Policy Agent Lambda function to the fingerprint value.

        Set the environment variable PTY_DATASTORE_KEY in the Policy Agent Function App to the fingerprint value.

        Set the variable in Policy Agent main.tf pty_datastore_key to the fingerprint value and apply the changes.

    2. Retrieve the PPC CA Certificate

      To obtain the CA certificate from PPC:

      kubectl -n api-gateway get secret ingress-certificate-secret -o jsonpath='{.data.ca\.crt}' | base64 -d > CA.pem
      

      Use the ProtegrityCA.pem that was returned as described in Policy Agent Installation.

    3. Configure the PPC Address

      Use the PPC address in place of the ESA IP address wherever required in your configuration.

    Log Forwarder Setup with PPC

    • The Log Forwarder will proceed without certificates and will print a warning if PTY_ESA_CA_SERVER_CERT is not provided.
    • No additional certificate or CA configuration is needed for PPC.

    2 - Sample Snowflake External Function

    Sample Snowflake External Function definitions and calls for tokenization data elements.

    Appendix A. Sample Snowflake External Function

    Method: Tokenization

    Type: ALPHA

     

    Snowflake Data Types

    Snowflake Max Size

    Protegrity Max Size

    VARCHAR

    16M (16,777,216 bytes)

    4K (4,096 bytes)

    CHAR

    STRING

    TEXT

     

    External Function Sample Definitions:

    CREATE SECURE EXTERNAL FUNCTION PTY_PROTECT_ALPHA ( val varchar ) 
      RETURNS varchar 
      NULL 
      IMMUTABLE 
      COMMENT = 'Protects using an ALPHA data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
      'X-Protegrity-HCoP-Rules'=
      '{"jsonpaths": [{"op_type":"PROTECT","data_element":"TOK_ALPHA"}]}'
      )
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    
    CREATE SECURE EXTERNAL FUNCTION PTY_UNPROTECT_ALPHA ( val varchar ) 
      RETURNS varchar 
      NULL 
      IMMUTABLE 
      COMMENT = 'Unprotects using an ALPHA data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"UNPROTECT","data_element":"TOK_ALPHA"}]}'
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    

     

    Sample EF Calls:

    SELECT PTY_PROTECT_ALPHA ('Hello World')
    
    SELECT PTY_UNPROTECT_ALPHA('rfDtw sLMJK');
    

     

    Snowflake Masking Policy example:

    create or replace masking policy alpha_policy as (val string) returns string -> 
     case 
     when current_role() in ('ACCOUNTADMIN') then PTY_UNPROTECT_ALPHA(val) 
     else val 
    end;
    
    alter table pii_data modify column field01 set masking policy alpha_policy; 
    alter table pii_data modify column field01 unset masking policy;
    

    Method: Tokenization

    Type: NUMERIC

     

    Snowflake Data Types

    Snowflake Max Size

    Protegrity Max Size

    NUMBER

     

     

    DECIMAL

    INTEGER

    DOUBLE

     

    External Function Sample Definitions:

    CREATE SECURE EXTERNAL FUNCTION PTY_PROTECT_NUMERIC ( val number ) 
      RETURNS number 
      NULL 
      IMMUTABLE 
      COMMENT = 'Protects using a NUMERIC data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"PROTECT","data_element":"TOK_NUMERIC"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    
    CREATE SECURE EXTERNAL FUNCTION PTY_UNPROTECT_NUMERIC ( val number) 
      RETURNS number 
      NULL 
      IMMUTABLE 
      COMMENT = 'Unprotects using a NUMERIC data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"UNPROTECT","data_element":"TOK_NUMERIC"}]}'
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    

     

    Sample EF Calls:

    SELECT PTY_PROTECT_NUMERIC ('123456789');
    
    SELECT PTY_UNPROTECT_NUMERIC ('752513497');
    

     

    Snowflake Masking Policy example:

    create or replace masking policy num_policy as (val number) returns number -> 
     case 
     when current_role() in ('ACCOUNTADMIN') then PTY_UNPROTECT_NUMERIC(val) 
     else val 
    end;
    
    alter table pii_data modify column field02 set masking policy num_policy; 
    alter table pii_data modify column field02 unset masking policy;
    

    Method: Tokenization

    Type: DATE YYYY-MM-DD

     

    Snowflake Data Types

    Snowflake Max Size

    Protegrity Max Size

    DATE (any supported format)

    10 bytes

    10 bytes

     

    External Function Sample Definitions:

    CREATE SECURE EXTERNAL FUNCTION PTY_PROTECT_DATEYYYYMMDD ( val date ) 
      RETURNS date 
      NULL 
      IMMUTABLE 
      COMMENT = 'Protects using a Date data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"PROTECT","data_element":"TOK_DATEYYYYMMDD"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    
    CREATE SECURE EXTERNAL FUNCTION PTY_UNPROTECT_DATEYYYYMMDD ( val date ) 
      RETURNS date 
      NULL 
      IMMUTABLE 
      COMMENT = 'Unprotects using a Date data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"UNPROTECT","data_element":"TOK_DATEYYYYMMDD"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    

    Sample EF Calls:

    SELECT PTY_PROTECT_DATEYYYYMMDD ('2020-12-31');
    
    SELECT PTY_UNPROTECT_DATEYYYYMMDD('0653-06-01');
    
    SELECT PTY_PROTECT_DATEYYYYMMDD ('31-DEC-2020');*
    
    SELECT PTY_UNPROTECT_DATEYYYYMMDD('01-JUN-0653');*
    
    SELECT PTY_PROTECT_DATEYYYYMMDD('12/31/2020');*
    
    SELECT PTY_UNPROTECT_DATEYYYYMMDD('06/01/0653');*
    
    SELECT PTY_PROTECT_DATEYYYYMMDD (current_date);
    

     

    Snowflake Masking Policy example:

    create or replace masking policy date_policy as (val date) returns date -> 
     case 
     when current_role() in ('ACCOUNTADMIN') then PTY_UNPROTECT_DATEYYYYMMDD (val) 
     else val 
    end;
    
    alter table pii_data modify column field11 set masking policy date_policy; 
    alter table pii_data modify column field11 unset masking policy;
    
    *: Automatic cast to YYYY-MM-DD, no need to make any conversions. The output is always in the YYYY-MM-DD format

    Cutover Dates of the Proleptic Gregorian Calendar: no issues (no conversions performed by Snowflake)

    Method: Tokenization

    Type: DATETIME

     

    Snowflake Data Types

    Snowflake Max Size

    Protegrity Max Size

    DATE

    10 bytes

    29 bytes

    DATETIME

    29 bytes

    TIMESTAMPNTZ*

    TIMESTAMP_NTZ*

    TIMESTAMP WITHOUT TIME ZONE*

     

    External Function Sample Definitions:

    CREATE SECURE EXTERNAL FUNCTION PTY_PROTECT_DATETIME ( val timestamp ) 
      RETURNS timestamp 
      NULL 
      IMMUTABLE 
      COMMENT = 'Protects using a TIMESTAMP data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"PROTECT","data_element":"TOK_DATETIME"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    
    CREATE SECURE EXTERNAL FUNCTION PTY_UNPROTECT_DATETIME ( val timestamp ) 
      RETURNS timestamp 
      NOT NULL 
      IMMUTABLE 
      COMMENT = 'Unprotects using a TIMESTAMP data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"UNPROTECT","data_element":"TOK_DATETIME"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    

    Sample EF Calls:

    SELECT PTY_PROTECT_DATETIME('2010-10-25');
    
    SELECT PTY_UNPROTECT_DATETIME('0845-04-04');
    
    SELECT PTY_PROTECT_DATETIME('2010-10-25 10:45:33');
    
    SELECT PTY_UNPROTECT_DATETIME('0845-04-04 10:45:33');
    
    SELECT PTY_PROTECT_DATETIME('2010-10-25 10:45:33.123');
    
    SELECT PTY_UNPROTECT_DATETIME('0845-04-04 10:45:33.123');
    
    SELECT PTY_PROTECT_DATETIME(current_date);
    
    SELECT PTY_PROTECT_DATETIME(cast(current_timestamp as TIMESTAMPNTZ));
    

     

    Snowflake Masking Policy example:

    create or replace masking policy datetime_policy as (val timestampntz) returns timestampntz -> 
     case 
     when current_role() in ('ACCOUNTADMIN') then PTY_UNPROTECT_DATETIME (val) 
     else val 
    end;
    
    alter table pii_data modify column field12 set masking policy datetime_policy; 
    alter table pii_data modify column field12 unset masking policy;
    
    *: Default TIMESTAMP in Snowflake includes Time Zone – not supported by Protegrity’s DATETIME data element

    Method: Tokenization

    Type: DECIMAL

     

    Snowflake Data Types

    Snowflake Max Size

    Protegrity Max Size

    NUMBER(N,M)

    38 digits

    36 digits

    NUMERIC(N,M)*

    DECIMAL(N,M)*

     

    External Function Sample Definitions:

    CREATE SECURE EXTERNAL FUNCTION PTY_PROTECT_DECIMAL ( val NUMBER(38,6) ) 
      RETURNS NUMBER(38,6) 
      NULL 
      IMMUTABLE 
      COMMENT = 'Protects using a DECIMAL data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"PROTECT","data_element":"TOK_DECIMAL"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    
    CREATE SECURE EXTERNAL FUNCTION PTY_UNPROTECT_DECIMAL ( val NUMBER(38,6) ) 
      RETURNS NUMBER(38,6) 
      NULL 
      IMMUTABLE 
      COMMENT = 'Unprotects using a DECIMAL data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"UNPROTECT","data_element":"TOK_DECIMAL"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    

    Sample EF Calls:

    SELECT PTY_PROTECT_DECIMAL (12345678.99);
    
    SELECT PTY_UNPROTECT_DECIMAL (21872469.760000);
    

     

    Snowflake Masking Policy example:

    create or replace masking policy decimal_policy as (val NUMBER(38,6)) returns NUMBER(38,6)-> 
     case 
     when current_role() in ('ACCOUNTADMIN') then PTY_UNPROTECT_DECIMAL (val) 
     else val 
    end;
    
    alter table pii_data modify column field13 set masking policy decimal_policy; 
    alter table pii_data modify column field13 unset masking policy;
    
    *: Synonymous with NUMBER

    Method: Tokenization

    Type: INTEGER

     

    Snowflake Data Types

    Snowflake Max Size

    Protegrity Max Size

    NUMBER

    38 digits

    2 bytes

    4 bytes

    8 bytes

    NUMERIC*

    INT*

    INTEGER*

    BIGINT*

    SMALLINT*

    TINYINT*

    BYTEINT*

     

    External Function Sample Definitions:

    CREATE SECURE EXTERNAL FUNCTION PTY_PROTECT_INTEGER ( val NUMBER ) 
      RETURNS NUMBER
      NULL 
      IMMUTABLE 
      COMMENT = 'Protects using an INTEGER data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"PROTECT","data_element":"TOK_INTEGER"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    
    CREATE SECURE EXTERNAL FUNCTION PTY_UNPROTECT_INTEGER ( val NUMBER ) 
      RETURNS NUMBER 
      NOT NULL 
      IMMUTABLE 
      COMMENT = 'Unprotects using an INTEGER data element'  
      API_INTEGRATION = REPLACE_WITH_YOUR_API_INTEGRATION_ID 
      HEADERS =(  
        'X-Protegrity-HCoP-Rules'=
        '{"jsonpaths":[{"op_type":"UNPROTECT","data_element":"TOK_INTEGER"}]}'    
      ) 
      CONTEXT_HEADERS = ( current_user, current_timestamp, current_account ) 
      AS '<api_gateway_protect_service_url>/pty/snowflake';
    

    Sample EF Calls:

    SELECT PTY_PROTECT_INTEGER (123456789);
    
    SELECT PTY_UNPROTECT_INTEGER (1104108887);
    

     

    Snowflake Masking Policy example:

    create or replace masking policy int_policy as (val NUMBER ) returns NUMBER -> 
     case 
     when current_role() in ('ACCOUNTADMIN') then PTY_UNPROTECT_INTEGER (val) 
     else val 
    end;
    
    alter table pii_data modify column field14 set masking policy int_policy; 
    alter table pii_data modify column field14 unset masking policy;
    
    *: Synonymous with NUMBER, except that precision and scale cannot be specified \(i.e. always defaults to NUMBER\(38, 0\)\)

    **Recommended approach for protecting whole numbers fields in Snowflake

    When values are…then use the following Data Element:
    Between -32768 and 32767INTEGER (2 bytes)
    Between -2147483648 and 2147483647INTEGER (4 bytes)
    Between -9223372036854775808 and 9223372036854775807INTEGER (8 bytes)
    < -9223372036854775808 or > 9223372036854775807DECIMAL

    When in doubt, use DECIMAL for any numeric range.

    3 - Configuring Regular Expression to Extract Policy Username

    Example configurations for user extraction with regular expressions

    Configuring Regular Expression to Extract Policy Username

    Cloud Protect Cloud Function exposes USERNAME_REGEX configuration to allow extraction of policy username from user in the request.

    • USERNAME_REGEX Cloud Function Environment configuration

      The USERNAME_REGEX environment variable can be set to contain regular expression with one capturing group. This group is used to extract the username. Examples below show different regular expression values and the resulting policy user.

    USERNAME_REGEX

    User in the request

    Effective Policy User

    Not Set

    user@domain.com

    user@domain.com

    service-account-user@project-id.iam.gserviceaccount.com

    service-account-user@project-id.iam.gserviceaccount.com

    ^(.*)@.*$
    

    service-account-user@project-id.iam.gserviceaccount.com

    service-account-user

    user@domain.com

    user

    4 - Associating ESA Data Store With Cloud Protect Agent

    ESA controls policy access by mapping server IPs to data stores, registering a node when an agent requests a policy and its IP is identified.

    ESA controls which policy is deployed to protector using concept of data store. A data store may contain a list of IP addresses identifying servers allowed to pull the policy associated with that specific data store. Data store may also be defined as default data store, which allows any server to pull the policy, provided it does not belong to any other data stores. Node registration occurs when the policy server (in this case the policy agent) makes a policy request to ESA, where the agent’s IP address is identified by ESA.

    Policy agent function source IP address used for node registration on ESA depends on ESA hubcontroller configuration ASSIGN_DATASTORE_USING_NODE_IP and the PTY_ADDIPADDRESSHEADER configuration exposed by the agent function.

    The function service uses multiple network interfaces, internal network interface with ephemeral IP range of 169.254.x.x and external network interface with IP range described in Function app outbound IP addresses section under function configuration. By default, when agent function is contacting ESA to register node for policy download, ESA uses agent function outbound IP address. This default behavior is caused by the default ESA hubcontroller configuration ASSIGN_DATASTORE_USING_NODE_IP=false and agent default configuration PTY_ADDIPADDRESSHEADER=yes.

    In some cases, when there is a proxy server between the ESA and agent function, the desirable ESA configuration is ASSIGN_DATASTORE_USING_NODE_IP=true. and PTY_ADDIPADDRESSHEADER=no which will cause the ESA to use proxy server IP address.

    The table below shows how the hubcontroller and agent settings will affect node IP registration on ESA.

    Agent source IPAgent Function Outbound IPProxy IPESA config - ASSIGN_DATASTORE_USING_NODE_IPAgent function config - PTY_ADDIPADDRESSHEADERAgent node registration IP
    169.254.144.8120.75.43.207No Proxytrueyes169.254.144.81
    trueno20.75.43.207
    falseyes
    falseno
    169.254.144.8120.75.43.20734.230.42.110trueyes169.254.144.81
    trueno34.230.42.110
    falseyes
    falseno

    5 - Undeliverable Audit Log Recovery

    Audit log recovery procedures

      Protegrity Cloud Protect Log Forwarder installation provides a solution to recover undelivered audit logs. Reasons for undeliverable logs may include:

      • Changes to network configuration in ESA or cloud provider (VPC, firewall, certificate rotation, service user credentials)
      • Log Forwarder IAM Service Account permissions
      • Log Forwarder Cloud Run Function configuration
      • Disruption in cloud provider service

      Log Forwarder Dead Letter Pub/Sub Architecture

      Log Forwarder is triggered by pub/sub events generated by Protect Functions. If Log Forwarder is unable to reach ESA to deliver the logs, they are pushed to a dead letter pub/sub topic. Dead letter pub/sub topic is created when installing the Log Forwarder with the service installation script. See Install Log Forwarder Function via Terraform Scripts for dead letter topic configuration options and naming conventions.

      Logs are not delivered to ESA. Undelivered audit logs are sent to a dead letter pub/sub topic.

      Monitoring Undelivered Logs

      Logs pushed to the dead letter pub/sub topic will be purged and no longer recoverable when specified dlq_topic_message_retention_duration has been reached. Monitoring the dead letter topic is recommended to ensure timely recovery of audit messages before they are permanently lost. Consult the GCP monitoring alerts documentation for setting up alerts based on pub/sub topic metrics.

      Protegrity recommends creation of an additional Log Forwarder installation in the case where logs are not delivered to ESA, as described in Log Forwarder Dead Letter Pub/Sub Architecture.

      Audit log recovery using new log forwarder installation

      Steps to recover audit logs using new Log Forwarder installation:

      1. Create a second Log Forwarder installation (Log Forwarder 2 in the above diagram) for processing undelivered logs. Value for audit_log_dead_letter_topic in the terraform script should be set to null during installation.

      2. Configure and test newly installed Log Forwarder to verify ESA connectivity. See Install Log Forwarder Function via Terraform Scripts for installation instructions.

      3. Identify the dead letter pub/sub topic (DLQ 1 in the above diagram) resource name by running command

        terraform output
        

        for the Log Forwarder which failed to deliver logs (Log Forwarder as described in Log Forwarder Dead Letter Pub/Sub Architecture). Note the value for audit_log_dlq_topic.

      4. Set audit_log_dead_letter_topic in the new Log Forwarder (Log Forwarder 2 in the above diagram) terraform installation script to the value of audit_log_dlq_topic identified in previous step. Apply the changes with terraform apply.

      5. Monitor the new Log Forwarder function logs for any failures.

      Recovering Logs in Dead Letter Topic (Alternative)

      When the recommended method of for recovery described in Recovering Logs in Dead Letter Topic (Recommended) is not an option, you may use the existing Log Forwarder to reprocess undelivered logs.

      Audit log recovery using existing log forwarder installation

      Steps to recover audit logs using existing Log Forwarder installation:

      • Fix any configuration errors causing the Log Forwarder to fail. Verify audit logs are being transmitted successfully to ESA.

      • Identify the dead letter pub/sub topic (DLQ 1 in the above diagram) resource name by running command

        terraform output
        

        for the Log Forwarder. Note the value for audit_log_dlq_topic.

      • Set audit_log_dead_letter_topic in the terraform installation script to the value of audit_log_dlq_topic identified in previous step. Apply the changes with

        terraform apply
        
      • When audit logs have been transmitted to ESA, revert setting audit_log_dead_letter_topic to null Apply the changes with

        terraform apply