Skip to main content

Salesforce DAGs

Overview 


The DAGs produced in this section were some of the first DAGs made during the internship. The DAGs here are made for Contact objects and Account objects, which are native to Salesforce. The DAGs originally were made during May-June of 2026 and intended for use in the Sandbox environment which only used test data. As of July 31st, 2026, the DAGs are being refit to function in a production environment.

All of the code can be accessed in AWS and as well in the GitHub repository. 

The general structure of each DAG is the following, each DAG follows an "ETL" or Extract, Transfer, Load design. Since PostgreSQL was chosen as one of the endpoints during the internship, data is fed between PostgreSQL or Salesforce and then fed into the intermediary service of Airflow. In Airflow, the data is "transformed" in whatever manner is necessary, and then is fed to the other service. If data was taken from PostgreSQL, then it would typically flow back out to Salesforce, and if data was taken from Salesforce it would generally go back to PostgreSQL. 

Many of the DAGs contain their own respective documentation, however additional documentation is added here such that they can be understood at a higher level and more context can be given with respect to their uses in Salesforce. 


Visual Studio Code AWS Extension

The Visual Studio Code (VSC) extension for AWS makes modifying and creating existing DAGs extremely simple. The utility allows users to create, delete, upload, and modify files in AWS' file hierarchy as if it were a normal file explorer in VSC. For example consider the current working directory for DAGs:

image.png

More information about the plugin can be found here. Installing the plugin in VSC and working with it is relatively simple. In VSC, the extension can easily be found by searching "AWS" into the extension marketplace. The top result "AWS Toolkit" should be installed. 

image.png

Once installed, there will appear an "AWS" icon on the bottom left sidebar of VSC. Click on it. 

In order to sign in once the sign in process is configured, begin by heading into myapps in URI's SSO and select AWS. Next, on the bottom left hand corner of VSC there will appear a red bar. Click on that bar.

image.png

After clicking on the bar, a menu will bring up the option to access "IAM Identity Center", click on it. This will bring up a dialogue box with the option to open a link to AWS which will verify your credentials and permit you access to AWS' services through VSC again.

image.png

Once the site has been correctly accessed, the following screen will appear, which will allow the user to close it and return to VSC.

image.png

Then, click IAM Identity Center again. If you have the corresponding permissions for AWS, then you should be able to hit AWS Full Permission, which at least should give you full access to AWS. Note that you will not be able to run Airflow inside this environment, merely modify files, delete them, and upload files in AWS from your VSC environment. 

I have opted for the following file hierarchy for the Airflow environment (this will not change how it is viewed and accessed from inside Airflow) since it separates DAGs into their respective functions quite well:

S3 bucket (foundational-etl-poc-bucket)
|___ requirements.txt
|___ dags/
        |___ create_tables/
		|___ salesforce_dags/
                      |___ account_dags/
                      |___ contact_dags/
		|___ affinaquest_dags/
    		|___ education_dags/

 

 


Bulk API

In Salesforce, there are four primary bulk operations, insertion, deletion, hard delete, updating, and upserting (update+inserting). Insertion adds a new entry to salesforce given some relevant fields about the object.

An object hard deleted in Salesforce is immediately deleted, whereas an object which is deleted in Salesforce lingers in the recycle bin for 15 days.

Airflow has bulk methods which make insertion and deletion of massive amounts of records efficient. I opted to use Simple Salesforce, which also has bulk operators that can be used in Airflow as well. For Further information on Bulk APIs in Simple Salesforce can be found here:
https://github.com/simple-salesforce/simple-salesforce

Rather than adding 50 objects individually, many are added at the same time. Each object in Salesforce has bulk methods. The bulk methods are all essentially identical in how they operate.


Test Connection DAG

This DAG simply tests that there is an existing Airflow connection and is entitled "salesforce_test_dag". This DAG is useful since it simply reads from Salesforce, which proves that Salesforce is connected without making any unexpected modifications to it or PostgreSQL. In the event that a connection must be re-established between Airflow and Salesforce, this DAG can be used to test that the connection is valid safely

The DAG does not follow the ETL structure as it is just used to verify that a connection exists.

from datetime import datetime
from airflow.decorators import dag, task
from airflow.providers.salesforce.hooks.salesforce import SalesforceHook

@dag(
    dag_id="salesforce_test_dag",
    schedule=None,
    start_date=datetime(2026, 1, 1),
    catchup=False,
)
def salesforce_test_dag():

    @task
    def test_connection():
        hook = SalesforceHook()
        
        # Test with a simple query
        result = hook.make_query("SELECT Id, Name FROM Account LIMIT 5")
        
        print(f"Successfully connected to Salesforce!")
        print(f"Retrieved {len(result['records'])} accounts")
        
        for record in result["records"]:
            print(f"  - {record['Name']}")
        
        return result["records"]

    test_connection()

salesforce_test_dag()=6.3.0



Create Table DAGs

These aren't much DAGs as much as they are files inside Airflow which are run following executions of DAGs for the corresponding objects. The Contact object has an associated PostgreSQL table for extracting information, and for loading in Contact info from salesforce. These tables are respectively known as "contact_extract" and "contact_load" For the Accounts object there is a similar associated "account_extract" and "account_load" table in PostgreSQL.

Contact DAGs

Contact objects in Salesforce represent an individual associated with a business account, and as the name would suggest, contains that person's name, phone number, email address, and other useful fields by which that person can be contacted. 

Bulk

Currently the only fields supported in the Contact InsertDAGs DAGare, "FirstName", "LastName", "Email", and "MobilePhone". All of these fields are self explanatory. By default, every object in Salesforce has its own unique ID, which is unique to each object. There is no strict uniqueness on any of these fields, and duplicate objects with the exact same fields can exist for Contacts.

An object's ID can be obtained via a SOQL query. SOQL is Saleforce's Query Language, which is similar to SQL in many respects but with notable differences such as lacking any wildcard operator ( the symbol "*", for instance in the query "SELECT * From Contacts" to select all fields from Contacts ). It is thus necessary to determine the identity of an object by writing queries which will select fields that should be unique in general, like a phone number or an email, or to match as many fields as possible against the objects. 

Using a single field like a phone number or email has the benefit of making a much more readable and plain query to work with, whereas using all fields makes selection of elements much quicker and queries easier to write. Due to general limitations on the size of queries in SOQL of 100,000 characters, and for more readable queries, I opted to only select the emails of each test account. 

PostgreSQL For Contacts 

There are two tables in the "mock_snowflake" PostgreSQL database for Contacts, namely a "contacts_extract" table for DAGs which begin in PostgreSQL and move data into Salesforce, and a "contacts_load" for DAGs which start in Salesforce and end in PostgreSQL. The queries used to write these tables are respectively, and are executed in Airflow from the "build_contacts_tables" DAG.

CREATE TABLE contacts_extract (
        FirstName VARCHAR(255), 
        LastName VARCHAR(255),
        Email VARCHAR(255),
        MobilePhone VARCHAR(20)
        );
CREATE TABLE contacts_load (
        FirstName VARCHAR(255), 
        LastName VARCHAR(255),
        Email VARCHAR(255),
        MobilePhone VARCHAR(20)
        );

Here is the resulting data for "contacts_extract" after it has been created:

snowflake_mock=# SELECT * FROM contacts_extract;
  firstname   |  lastname   |    email    |   mobilephone
--------------+-------------+-------------+-----------------
 1_First_Name | 1_Last_Name | 1@gmail.com | +1-111-111-1111
 2_First_Name | 2_Last_Name | 2@gmail.com | +2-222-222-2222
 3_First_Name | 3_Last_Name | 3@gmail.com | +3-333-333-3333
 4_First_Name | 4_Last_Name | 4@gmail.com | +4-444-444-4444
(4 rows)
snowflake_mock=# SELECT * FROM contacts_load;
firstname | lastname | email | mobilephone
-----------+----------+-------+-------------
(0 rows)

In order to reset both tables, run the "build_contacts_tables" DAG. It will drop either of the tables if detected, and reconstruct both tables with the above associated fields. Secondly, it will add in the default information into "contacts_extract".

 

When working with the Upsert, Update, and Delete DAGs, it is important to note that in order to modify or remove an existing entry from Salesforce, there needs to be a way to identify a corresponding entry/entries that can be changed. 

Bulk Contact Upsert DAG

Bulk Contact Update DAG

Bulk Contact Delete DAG



Account DAGs

Bulk Account Insert DAG

Bulk Account Upsert DAG

Bulk Account Update DAG

Bulk Account Delete DAG