Create and configure a Snowflake sink connector for Apache Kafka®
The Apache Kafka Connect® Snowflake sink connector moves data from Aiven for Apache Kafka® topics to a Snowflake database. Configure access in Snowflake before creating the connector in Aiven.
Aiven provides two separate connectors:
- Snowflake Sink: version 3.x, which uses the
com.snowflake.kafka.connector.SnowflakeSinkConnectorclass. Snowflake refers to this version as the classic connector. It's stable and supported, but Snowflake plans to deprecate it. - Snowflake v4 Sink: version 4.2.1, which uses the
com.snowflake.kafka.connector.SnowflakeStreamingSinkConnectorclass and the Snowpipe Streaming high-performance architecture.
Snowflake v4 Sink is in early availability.
Aiven doesn't automatically upgrade existing Snowflake Sink connectors to Snowflake v4 Sink. Version 4.x isn't backward compatible with 3.x. To move an existing connector to 4.x, review Migrate from 3.x to 4.x before creating the replacement.
Prerequisites
- An Aiven for Apache Kafka service with Apache Kafka Connect enabled, or a dedicated Kafka Connect cluster.
- Access to a Snowflake account with permissions to create users and roles and grant access to the target database and schema.
- OpenSSL installed locally to generate a key pair.
- The connection details listed in the following section.
Connection details
Collect the following values:
SNOWFLAKE_URL: your Snowflake account URL, for example,myorg-myaccount.snowflakecomputing.com. For details, see Snowflake account identifiers.SNOWFLAKE_PRIVATE_KEY: the private key for the userSNOWFLAKE_PRIVATE_KEY_PASSPHRASE: Optional: the private key passphrase. Required only for encrypted private keys.SNOWFLAKE_DATABASE: the target databaseSNOWFLAKE_SCHEMA: the target schemaTOPIC_LIST: a comma-separated list of Kafka topics
For Avro messages, also collect the following values:
APACHE_KAFKA_HOST: the Aiven for Apache Kafka service hostSCHEMA_REGISTRY_PORT: the Schema Registry portSCHEMA_REGISTRY_USER: the Schema Registry userSCHEMA_REGISTRY_PASSWORD: the Schema Registry password
To find your account URL in Snowsight, open the account selector, select View account details, and copy Account/Server URL.
For more information about configuration options, see the Snowflake Kafka Connector documentation.
Configure Snowflake
Set up key-pair authentication, create a Snowflake user and role, and grant access to the target database and schema.
Generate a key pair
Use OpenSSL to generate a 2048-bit RSA key pair:
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
rsa_key.p8contains the passphrase-protected private key.rsa_key.pubcontains the public key.
Use the private key as the SNOWFLAKE_PRIVATE_KEY value in the connector configuration.
For the key format the connector accepts, see
Snowflake key-pair authentication.
You only need the passphrase for an encrypted private key.
Create a user
-
Open Worksheets in Snowflake.
-
Use a role with privileges to create users, such as
SECURITYADMINorACCOUNTADMIN. -
Create a user for the connector:
CREATE USER aiven; -
Copy the contents of
rsa_key.pub, excluding the-----BEGIN PUBLIC KEY-----and-----END PUBLIC KEY-----lines. Remove line breaks. -
Set the user's public key:
ALTER USER aiven SET RSA_PUBLIC_KEY='PASTE_PUBLIC_KEY_HERE';
Create a role and assign it to the user
-
Create a role:
CREATE ROLE aiven_snowflake_sink_connector_role; -
Grant the role to the connector user:
GRANT ROLE aiven_snowflake_sink_connector_role TO USER aiven; -
Set the user's default role:
ALTER USER aiven SET DEFAULT_ROLE = aiven_snowflake_sink_connector_role;
Grant privileges on the target database and schema
Replace TESTDATABASE and TESTSCHEMA with your Snowflake database and schema names. Use
a role authorized to grant the privileges.
- Version 3.x
- Version 4.x
Version 3.x requires privileges for tables, stages, and pipes:
GRANT USAGE ON DATABASE TESTDATABASE TO ROLE aiven_snowflake_sink_connector_role;
GRANT USAGE ON SCHEMA TESTDATABASE.TESTSCHEMA TO ROLE aiven_snowflake_sink_connector_role;
GRANT CREATE TABLE ON SCHEMA TESTDATABASE.TESTSCHEMA TO ROLE aiven_snowflake_sink_connector_role;
GRANT CREATE STAGE ON SCHEMA TESTDATABASE.TESTSCHEMA TO ROLE aiven_snowflake_sink_connector_role;
GRANT CREATE PIPE ON SCHEMA TESTDATABASE.TESTSCHEMA TO ROLE aiven_snowflake_sink_connector_role;
Version 4.x is in
early availability
and has different privilege requirements than 3.x. Grant the following privileges on the
warehouse, database, and schema. Replace TESTWAREHOUSE with the warehouse the
connector uses:
GRANT USAGE ON WAREHOUSE TESTWAREHOUSE TO ROLE aiven_snowflake_sink_connector_role;
GRANT USAGE ON DATABASE TESTDATABASE TO ROLE aiven_snowflake_sink_connector_role;
GRANT USAGE ON SCHEMA TESTDATABASE.TESTSCHEMA TO ROLE aiven_snowflake_sink_connector_role;
If you let the connector create tables, or create pipes in the default pipe mode, also grant the corresponding privileges:
GRANT CREATE TABLE ON SCHEMA TESTDATABASE.TESTSCHEMA TO ROLE aiven_snowflake_sink_connector_role;
GRANT CREATE PIPE ON SCHEMA TESTDATABASE.TESTSCHEMA TO ROLE aiven_snowflake_sink_connector_role;
The connector always requires INSERT on destination tables. For an existing table,
grant it explicitly:
GRANT INSERT ON TABLE TESTDATABASE.TESTSCHEMA.TARGET_TABLE
TO ROLE aiven_snowflake_sink_connector_role;
If an existing table doesn't have a RECORD_METADATA column, the connector tries to add
it, which requires privileges to alter the table. To avoid this, add the
RECORD_METADATA VARIANT column to the table before you start the connector.
If you use a user-defined pipe, grant OPERATE on the existing pipe instead of
CREATE PIPE:
GRANT OPERATE ON PIPE TESTDATABASE.TESTSCHEMA.EXISTING_PIPE
TO ROLE aiven_snowflake_sink_connector_role;
Snowflake also recommends granting CREATE VIEW on the schema for its future error
table features.
Grant privileges directly to the connector role. Snowflake doesn't support inheriting them
through a role hierarchy. The 4.x requirements don't include CREATE STAGE.
For conditional grants and privilege details, see Configure Snowflake for connector 4.x.
Create a Snowflake sink connector configuration
Create a JSON file such as snowflake_sink.json. Choose the example for the connector
you're creating, then replace the placeholders with your connection details.
The following examples read Avro messages using Aiven's Karapace Schema Registry.
- Version 3.x
- Version 4.x
{
"name": "my-test-snowflake",
"connector.class": "com.snowflake.kafka.connector.SnowflakeSinkConnector",
"topics": "TOPIC_LIST",
"key.converter": "io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"key.converter.basic.auth.credentials.source": "USER_INFO",
"key.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"value.converter": "io.confluent.connect.avro.AvroConverter",
"value.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"value.converter.basic.auth.credentials.source": "USER_INFO",
"value.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"snowflake.url.name": "SNOWFLAKE_URL",
"snowflake.user.name": "aiven",
"snowflake.private.key": "SNOWFLAKE_PRIVATE_KEY",
"snowflake.private.key.passphrase": "SNOWFLAKE_PRIVATE_KEY_PASSPHRASE",
"snowflake.database.name": "SNOWFLAKE_DATABASE",
"snowflake.schema.name": "SNOWFLAKE_SCHEMA"
}
Version 4.x is in early availability. This example creates a new 4.2.1 connector. It isn't a migration configuration.
{
"name": "my-test-snowflake",
"connector.class": "com.snowflake.kafka.connector.SnowflakeStreamingSinkConnector",
"topics": "TOPIC_LIST",
"snowflake.topic2table.map": "TOPIC_NAME:TABLE_NAME",
"key.converter": "io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"key.converter.basic.auth.credentials.source": "USER_INFO",
"key.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"value.converter": "io.confluent.connect.avro.AvroConverter",
"value.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"value.converter.basic.auth.credentials.source": "USER_INFO",
"value.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"snowflake.url.name": "SNOWFLAKE_URL",
"snowflake.user.name": "aiven",
"snowflake.private.key": "SNOWFLAKE_PRIVATE_KEY",
"snowflake.private.key.passphrase": "SNOWFLAKE_PRIVATE_KEY_PASSPHRASE",
"snowflake.database.name": "SNOWFLAKE_DATABASE",
"snowflake.schema.name": "SNOWFLAKE_SCHEMA",
"snowflake.role.name": "aiven_snowflake_sink_connector_role",
"snowflake.validation": "client_side",
"snowflake.streaming.validate.compatibility.with.classic": "false"
}
For new 4.x connectors, set snowflake.streaming.validate.compatibility.with.classic
to false to disable migration validation. By default, the connector validates the
migration-related properties at startup and fails to start unless you set them
explicitly.
The example uses client_side validation to determine column types from the Avro
schema.
Version 4.x enables schematization by default, which maps record fields to individual
Snowflake columns. To retain the 3.x layout with RECORD_CONTENT and RECORD_METADATA
columns, set snowflake.enable.schematization to false.
Don't use this configuration unchanged if you're migrating from 3.x. Review the required compatibility and offset migration settings in the Snowflake migration guide first.
Configuration parameters
Connector and converter settings:
name: Unique connector nameconnector.class: Connector implementation for the selected versiontopics: Comma-separated topics to load into Snowflakesnowflake.topic2table.map: Maps Kafka topics to Snowflake tables. Specify comma-separatedtopic:tablepairs, for example,book:BOOK,product:PRODUCT. If omitted, the connector derives table names from Kafka topic names.key.converterandvalue.converter: Converters for Kafka message keys and values. The example usesio.confluent.connect.avro.AvroConverter.
Schema Registry settings for Avro:
key.converter.schema.registry.urlandvalue.converter.schema.registry.url: Schema Registry URL in the formhttps://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORTkey.converter.basic.auth.credentials.sourceandvalue.converter.basic.auth.credentials.source: Credentials source for basic authentication, set toUSER_INFOkey.converter.schema.registry.basic.auth.user.infoandvalue.converter.schema.registry.basic.auth.user.info: Schema Registry credentials in the formSCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD
Snowflake connection settings:
snowflake.url.name: Snowflake account URLsnowflake.user.name: Snowflake user for ingestionsnowflake.private.key: Private key used to authenticate the usersnowflake.private.key.passphrase: Optional: Passphrase protecting the private key. Required only for encrypted private keys.snowflake.database.name: Target databasesnowflake.schema.name: Target schema
Additional 4.x settings shown in the example:
snowflake.role.name: Role the connector uses to write data. The example uses the role you created in Configure Snowflake.snowflake.validation: Validation mode. Useclient_sideto validate against schema types before ingestion. The 4.x default isserver_side.snowflake.streaming.validate.compatibility.with.classic: Migration-related compatibility checks. Set tofalsefor new deployments, but not automatically for migrations.
For all 4.x settings, see Snowflake connector configuration.
Create a Snowflake sink connector
- Aiven Console
- Aiven CLI
-
In the Aiven Console, click your Aiven for Apache Kafka or Aiven for Apache Kafka Connect service.
-
Click Manage stream > Connectors.
-
Click Create connector if Apache Kafka Connect is already enabled on the service. If not, click Enable connector on this service.
To enable connectors:
- In the sidebar, click Service settings.
- In the Service management section, click Actions > Enable Kafka Connect.
-
In the list of sink connectors, find Snowflake Sink for 3.x or Snowflake v4 Sink for 4.x, then click Get started.
-
On the connector page, go to the Common tab.
-
Locate Connector configuration and click Edit.
-
Paste the configuration for the selected version from your
snowflake_sink.jsonfile. Replace all placeholders with actual values. -
Click Apply.
-
Click Create connector.
-
Verify the connector status on the Manage stream > Connectors page.
-
Confirm that data from the Apache Kafka topics appears in the target Snowflake database.
Create the connector using the Aiven CLI:
avn service connector create SERVICE_NAME @snowflake_sink.json
Replace the following:
SERVICE_NAME: name of your Aiven for Apache Kafka or Aiven for Apache Kafka Connect service@snowflake_sink.json: path to the JSON configuration for the selected connector version
Migrate from 3.x to 4.x
Snowflake connector 4.x isn't backward compatible with 3.x, and Aiven doesn't upgrade existing 3.x connectors automatically. To move to 4.x, create a 4.x connector as described in this section.
Before you migrate:
- Review the Snowflake migration guide for breaking changes, configuration requirements, and migration steps.
- Check the
snowflake.ingestion.methodsetting of your existing 3.x connector. It is eitherSNOWPIPE, which is the default, orSNOWPIPE_STREAMING. The offset migration procedure depends on this setting. - Prepare a new configuration that uses the
com.snowflake.kafka.connector.SnowflakeStreamingSinkConnectorclass. Setsnowflake.streaming.classic.offset.migrationtoskipunless your 3.x connector usesSNOWPIPE_STREAMING. Migrating fromSNOWPIPE_STREAMINGcan require special attention depending on your use case. See migrating from Snowpipe Streaming mode. - Test the migration procedure on a non-production service before you migrate production workloads.
To preserve offsets, Snowflake's migration procedure requires the same connector name for 3.x and 4.x. The 4.x connector then inherits the Kafka consumer group offsets and resumes where 3.x stopped. Aiven doesn't allow two connectors with the same name, so delete the 3.x connector before you create the 4.x connector. Follow Snowflake's instructions for your ingestion method, such as migrating from Snowpipe mode.
To migrate:
- Shut down the 3.x connector gracefully, then delete it. A forceful shutdown might prevent the offsets from advancing and cause duplicate records.
- For Snowpipe mode, wait until Snowflake finishes loading all staged data. Otherwise, data might arrive out of order.
- Deploy the 4.x connector with the same name on the same Aiven for Apache Kafka service as the 3.x connector. That service holds the consumer group offsets, so a connector on a different service doesn't resume from the 3.x offsets.
Ingestion stops from the time you delete the 3.x connector until the 4.x connector
starts. Start the 4.x connector before the consumer group offsets expire. The
offsets.retention.minutes default is 7 days, and 3 days for free and developer plans.
Downgrade from 4.x to 3.x
To go back to 3.x:
- Stop the 4.x connector.
- Create the 3.x connector again with the same name and configuration.
- Use the
RECORD_METADATAcolumn to find and remove duplicate records.
Expect duplicates after a downgrade, because 3.x and 4.x track offsets differently. For the full procedure and a query to remove duplicates, see Downgrading from v4 to v3.
Examples
The following examples show how to configure the Snowflake sink connector for Avro and JSON messages. The Avro example covers both versions. The JSON example covers 3.x only, because 4.x uses a different class and can create a different Snowflake table structure by default. Review the 4.x configuration before you adapt the JSON example for 4.x.
Create a Snowflake sink connector for an Avro topic
This example uses:
- Connector name:
my_snowflake_sink - Kafka topic:
test - Snowflake database:
testdb - Snowflake schema:
testschema - Snowflake URL:
myorg-myaccount.snowflakecomputing.com - Snowflake user:
aiven
Replace the Snowflake URL, private key, passphrase, and Schema Registry placeholders with your own values.
- Version 3.x
- Version 4.x
{
"name": "my_snowflake_sink",
"connector.class": "com.snowflake.kafka.connector.SnowflakeSinkConnector",
"key.converter": "io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"key.converter.basic.auth.credentials.source": "USER_INFO",
"key.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"value.converter": "io.confluent.connect.avro.AvroConverter",
"value.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"value.converter.basic.auth.credentials.source": "USER_INFO",
"value.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"topics": "test",
"snowflake.url.name": "myorg-myaccount.snowflakecomputing.com",
"snowflake.user.name": "aiven",
"snowflake.private.key": "SNOWFLAKE_PRIVATE_KEY",
"snowflake.private.key.passphrase": "SNOWFLAKE_PRIVATE_KEY_PASSPHRASE",
"snowflake.database.name": "testdb",
"snowflake.schema.name": "testschema"
}
{
"name": "my_snowflake_sink",
"connector.class": "com.snowflake.kafka.connector.SnowflakeStreamingSinkConnector",
"key.converter": "io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"key.converter.basic.auth.credentials.source": "USER_INFO",
"key.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"value.converter": "io.confluent.connect.avro.AvroConverter",
"value.converter.schema.registry.url": "https://APACHE_KAFKA_HOST:SCHEMA_REGISTRY_PORT",
"value.converter.basic.auth.credentials.source": "USER_INFO",
"value.converter.schema.registry.basic.auth.user.info": "SCHEMA_REGISTRY_USER:SCHEMA_REGISTRY_PASSWORD",
"topics": "test",
"snowflake.topic2table.map": "test:TEST",
"snowflake.url.name": "myorg-myaccount.snowflakecomputing.com",
"snowflake.user.name": "aiven",
"snowflake.private.key": "SNOWFLAKE_PRIVATE_KEY",
"snowflake.private.key.passphrase": "SNOWFLAKE_PRIVATE_KEY_PASSPHRASE",
"snowflake.database.name": "testdb",
"snowflake.schema.name": "testschema",
"snowflake.role.name": "aiven_snowflake_sink_connector_role",
"snowflake.validation": "client_side",
"snowflake.streaming.validate.compatibility.with.classic": "false"
}
Create a Snowflake sink connector 3.x for a topic with a JSON schema
This example reads JSON messages from the iot_measurements topic. Each message includes
an inline schema.
Sample message:
{
"schema": {
"type": "struct",
"fields": [
{ "type": "int64", "optional": false, "field": "iot_id" },
{ "type": "string", "optional": false, "field": "metric" },
{ "type": "int32", "optional": false, "field": "measurement" }
]
},
"payload": { "iot_id": 1, "metric": "Temperature", "measurement": 14 }
}
Another sample message:
{
"schema": {
"type": "struct",
"fields": [
{ "type": "int64", "optional": false, "field": "iot_id" },
{ "type": "string", "optional": false, "field": "metric" },
{ "type": "int32", "optional": false, "field": "measurement" }
]
},
"payload": { "iot_id": 2, "metric": "Humidity", "measurement": 60 }
}
Each JSON record includes a full schema, which adds overhead. To avoid repeating the schema, use Avro with Aiven's Karapace Schema Registry.
Create a connector configuration and replace the placeholders with your values:
{
"name": "my-test-snowflake-1",
"connector.class": "com.snowflake.kafka.connector.SnowflakeSinkConnector",
"value.converter": "org.apache.kafka.connect.json.JsonConverter",
"value.converter.schemas.enable": "true",
"topics": "iot_measurements",
"snowflake.url.name": "SNOWFLAKE_URL",
"snowflake.user.name": "aiven",
"snowflake.private.key": "SNOWFLAKE_PRIVATE_KEY",
"snowflake.private.key.passphrase": "SNOWFLAKE_PRIVATE_KEY_PASSPHRASE",
"snowflake.database.name": "SNOWFLAKE_DATABASE",
"snowflake.schema.name": "SNOWFLAKE_SCHEMA"
}
topics: Kafka topic the connector reads from and writes to Snowflake.value.converter: Converter for message values. The example uses the Kafka Connect JSON converter.value.converter.schemas.enable: Set totruewhen each message contains aschemaand apayloadfield. The example sets this explicitly for clarity.
The configuration has no key converter because the example messages have no keys.