From 857dc548aa277c212d2de2bd25d10d5892872f8e Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Mon, 26 Oct 2026 12:18:44 +0530 Subject: [PATCH 1/4] add connection string and shared key authentication --- docs/writers/parquet/config.mdx | 123 +++++++++++++++++++++- static/img/docs/s3/azure-conn-string.webp | Bin 0 -> 12842 bytes static/img/docs/s3/azure-shared-key.webp | Bin 0 -> 15724 bytes 3 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 static/img/docs/s3/azure-conn-string.webp create mode 100644 static/img/docs/s3/azure-shared-key.webp diff --git a/docs/writers/parquet/config.mdx b/docs/writers/parquet/config.mdx index cc06057fc..4c426d67f 100644 --- a/docs/writers/parquet/config.mdx +++ b/docs/writers/parquet/config.mdx @@ -1,12 +1,28 @@ --- -title: "OLake Parquet S3 Writer Setup & Configuration Guide" -description: Configure OLake to write Parquet files to AWS S3, GCS, or MinIO with detailed setup, IAM permissions, HMAC keys, and endpoint configurations +title: "OLake Parquet Writer Setup & Configuration Guide" +description: Configure the Amazon S3 connector to write Parquet files to S3-compatible storage (AWS S3, GCS, or MinIO) or Azure. sidebar_label: Config and SetUp sidebar_position: 2 --- +import TOCTabLinker from '@site/src/components/TOCTabLinker'; + # S3 Parquet +OLake Go writes Parquet through the Amazon S3 connector. **Storage Type** selects S3-compatible storage (AWS S3, GCS, or MinIO) or Azure. Before proceeding, review the [Getting Started](/docs/getting-started/quickstart) and [Installation](/docs/install/olake-ui) sections. + + + + + + + OLake Go supports writing in parquet format directly to S3. Before proceeding with the S3 Parquet destination, we recommend reviewing the [Getting Started](/docs/getting-started/quickstart) and [Installation](/docs/install/olake-ui) sections. --- @@ -50,4 +66,105 @@ OLake Go supports writing in parquet format directly to S3. Before proceeding wi S3 write test ``` This is used to verify that you have the necessary permissions to write to S3. -::: \ No newline at end of file +::: + + + + + +OLake Go writes Parquet to Azure when **Storage Type** is Azure on the Amazon S3 connector. Azure supports Connection String and Shared Key authentication. + +## Prerequisites + +Azure prerequisites will be added here. + +--- + +## Configuration + +Configure the following fields to write Parquet files to Azure. + +### Authentication + +It supports multiple authentication types. Select the authentication mechanism your storage account uses. + + + + + +To use **Connection String** authentication, create a storage account connection string in Azure first. Follow the steps in the [Configure Azure Storage connection strings](https://learn.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string) documentation before configuring OLake Go. + + + + + +![OLake S3 destination config fields and reference guide.](/img/docs/s3/azure-conn-string.webp) + +**Connection String Configuration Parameters:** + +| Parameter | Sample Value | Description | +|-----------|--------------|-------------| +| **Storage Type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | +| **Azure Auth Type**
`required` | `Connection String` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | +| **Azure Path** | `/data` | Path inside the container where data files are written. | +| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **Azure Connection String**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | Storage account connection string. Includes AccountName, AccountKey, and EndpointSuffix. | +| **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | + +
+ + + +:::note +CLI configuration for Connection String authentication will be added here. +::: + + + +
+ +
+ + + +To use **Shared Key** authentication, copy the storage account name and an account access key from Azure. Follow the steps in the [Manage storage account access keys](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage) documentation before configuring OLake Go. + + + + + +![OLake S3 destination config fields and reference guide.](/img/docs/s3/azure-shared-key.webp) + +**Shared Key Configuration Parameters:** + +| Parameter | Sample Value | Description | +|-----------|--------------|-------------| +| **Storage Type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | +| **Azure Auth Type**
`required` | `Shared Key` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **Azure Storage Account Name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | +| **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | +| **Azure Path** | `/data` | Path inside the container where data files are written. | +| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **Azure Storage Account Key**
`required` | `` | Access key for the Azure storage account. OLake Go uses this with the account name to sign Blob requests. | +| **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | + +
+ + + +:::note +CLI configuration for Shared Key authentication will be added here. +::: + + + +
+ +
+ +
+ +
+ +
diff --git a/static/img/docs/s3/azure-conn-string.webp b/static/img/docs/s3/azure-conn-string.webp new file mode 100644 index 0000000000000000000000000000000000000000..39d249631f1c004af58a3284e407d956636077c4 GIT binary patch literal 12842 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 Date: Mon, 26 Oct 2026 15:50:33 +0530 Subject: [PATCH 2/4] add sas token authentication --- docs/writers/parquet/config.mdx | 132 ++++++++++++++++++++++-- static/img/docs/s3/azure-sas-token.webp | Bin 0 -> 13396 bytes 2 files changed, 121 insertions(+), 11 deletions(-) create mode 100644 static/img/docs/s3/azure-sas-token.webp diff --git a/docs/writers/parquet/config.mdx b/docs/writers/parquet/config.mdx index 4c426d67f..13fce60a4 100644 --- a/docs/writers/parquet/config.mdx +++ b/docs/writers/parquet/config.mdx @@ -72,7 +72,7 @@ OLake Go supports writing in parquet format directly to S3. Before proceeding wi -OLake Go writes Parquet to Azure when **Storage Type** is Azure on the Amazon S3 connector. Azure supports Connection String and Shared Key authentication. +OLake Go writes Parquet to Azure when **Storage Type** is Azure on the Amazon S3 connector. Azure supports Connection String, Shared Key, and SAS Token authentication. ## Prerequisites @@ -92,7 +92,7 @@ It supports multiple authentication types. Select the authentication mechanism y -To use **Connection String** authentication, create a storage account connection string in Azure first. Follow the steps in the [Configure Azure Storage connection strings](https://learn.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string) documentation before configuring OLake Go. +To use **Connection String** authentication, copy a storage account access key in Azure and pass it to OLake Go inside one connection string. The string contains the account name, the key, and the endpoint suffix. OLake Go pulls the key out of the string and signs each Blob request with it. Follow the steps in the [Manage storage account access keys](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal) documentation to access the key. @@ -109,16 +109,39 @@ To use **Connection String** authentication, create a storage account connection | **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | | **Azure Path** | `/data` | Path inside the container where data files are written. | | **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | -| **Azure Connection String**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | Storage account connection string. Includes AccountName, AccountKey, and EndpointSuffix. | +| **Azure Connection String**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | One string that contains the account name, the access key, and the endpoint suffix. OLake Go pulls the key out of this string and signs each Blob request with it. | | **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. |
-:::note -CLI configuration for Connection String authentication will be added here. -::: +```json title="destination.json" +{ + "type": "PARQUET", + "writer": { + "storage_type": "Azure", + "azure_auth_type": "connection-string", + "azure_container_name": "olakeauthtest", + "azure_path": "/data", + "azure_endpoint": "https://.blob.core.windows.net/", + "azure_connection_string": "", + "max_file_size_mb": "" + } +} +``` + +**Connection String Configuration Parameters:** + +| Parameter | Sample Value | Description | +|-----------|--------------|-------------| +| **storage_type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | +| **azure_auth_type**
`required` | `Connection String` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **azure_container_name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | +| **azure_path** | `/data` | Path inside the container where data files are written. | +| **azure_endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **azure_connection_string**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | One string that contains the account name, the access key, and the endpoint suffix. OLake Go pulls the key out of this string and signs each Blob request with it. | +| **max_file_size_mb** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. |
@@ -128,7 +151,7 @@ CLI configuration for Connection String authentication will be added here. -To use **Shared Key** authentication, copy the storage account name and an account access key from Azure. Follow the steps in the [Manage storage account access keys](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage) documentation before configuring OLake Go. +To use **Shared Key** authentication, copy a storage account access key in Azure and give OLake Go the raw key and the account name as separate fields. OLake Go uses the key to sign each Blob request, and Azure checks the signature. Follow the steps in the [Manage storage account access keys](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal) documentation to access the key. @@ -146,16 +169,103 @@ To use **Shared Key** authentication, copy the storage account name and an accou | **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | | **Azure Path** | `/data` | Path inside the container where data files are written. | | **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | -| **Azure Storage Account Key**
`required` | `` | Access key for the Azure storage account. OLake Go uses this with the account name to sign Blob requests. | +| **Azure Storage Account Key**
`required` | `` | Raw storage account access key, passed separately from the account name. OLake Go uses the key to sign each Blob request. | | **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. |
-:::note -CLI configuration for Shared Key authentication will be added here. -::: +```json title="destination.json" +{ + "type": "PARQUET", + "writer": { + "storage_type": "Azure", + "azure_auth_type": "shared-key", + "azure_storage_account_name": "olakeadlsauthtest", + "azure_container_name": "warehouse", + "azure_path": "", + "azure_endpoint": "", + "azure_storage_account_key": "", + "max_file_size_mb": "" + } +} +``` + +**Shared Key Configuration Parameters:** + +| Parameter | Sample Value | Description | +|-----------|--------------|-------------| +| **storage_type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | +| **azure_auth_type**
`required` | `Shared Key` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **azure_storage_account_name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | +| **azure_container_name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | +| **azure_path** | `/data` | Path inside the container where data files are written. | +| **azure_endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **azure_storage_account_key**
`required` | `` | Raw storage account access key, passed separately from the account name. OLake Go uses the key to sign each Blob request. | +| **max_file_size_mb** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | + +
+ + + +
+ + + +To use **SAS Token** authentication, generate a shared access signature for your container in Azure and give OLake Go the token and the account name as separate fields. The token limits what OLake Go can do: it is scoped to a container, and its permissions and expiry can be set directly on the token or through a stored access policy. OLake Go appends the token to each Blob request, and Azure checks the signature and the permissions before allowing the request. Follow the steps in the [Grant limited access to Azure Storage](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview) resources using shared access signatures (SAS) documentation to create the token. + + + + + +![OLake S3 destination config fields and reference guide.](/img/docs/s3/azure-sas-token.webp) + +**SAS Token Configuration Parameters:** + +| Parameter | Sample Value | Description | +|-----------|--------------|-------------| +| **Storage Type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | +| **Azure Auth Type**
`required` | `SAS Token` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **Azure Storage Account Name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | +| **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container the SAS token is scoped to. The container must already exist and the token must allow writes. | +| **Azure Path** | `/data` | Path inside the container where data files are written. | +| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **Azure SAS Token**
`required` | `` | Shared access signature for the container, passed separately from the account name. OLake Go appends this token to each Blob request. | +| **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | + +
+ + + +```json title="destination.json" +{ + "type": "PARQUET", + "writer": { + "storage_type": "Azure", + "azure_auth_type": "sas-token", + "azure_storage_account_name": "olakedata", + "azure_endpoint": "https://olakedata.blob.core.windows.net/", + "azure_path": "/data", + "azure_sas_token": "spr=https&sv=2024-11-04&si=olake-rw&sr=c&sig=", + "azure_container_name": "warehouse", + "max_file_size_mb": 512 + } +} +``` + +**SAS Token Configuration Parameters:** + +| Parameter | Sample Value | Description | +|-----------|--------------|-------------| +| **storage_type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | +| **azure_auth_type**
`required` | `sas-token` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **azure_storage_account_name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | +| **azure_container_name**
`required` | `warehouse` | Name of an existing Azure storage container the SAS token is scoped to. The container must already exist and the token must allow writes. | +| **azure_path** | `/data` | Path inside the container where data files are written. | +| **azure_endpoint** | `https://olakedata.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **azure_sas_token**
`required` | `spr=https&sv=2024-11-04&si=olake-rw&sr=c&sig=` | Shared access signature for the container, passed separately from the account name. OLake Go appends this token to each Blob request. | +| **max_file_size_mb** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. |
diff --git a/static/img/docs/s3/azure-sas-token.webp b/static/img/docs/s3/azure-sas-token.webp new file mode 100644 index 0000000000000000000000000000000000000000..51d2bd27a57d703225540597f7144e87cef46bde GIT binary patch literal 13396 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 literal 0 HcmV?d00001 From 64c00a90ce2fada3c2f7107141bd9b03d4eba361 Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Mon, 26 Oct 2026 17:49:22 +0530 Subject: [PATCH 3/4] add prerequisites --- docs/writers/parquet/config.mdx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/writers/parquet/config.mdx b/docs/writers/parquet/config.mdx index 13fce60a4..a2497d398 100644 --- a/docs/writers/parquet/config.mdx +++ b/docs/writers/parquet/config.mdx @@ -74,9 +74,12 @@ OLake Go supports writing in parquet format directly to S3. Before proceeding wi OLake Go writes Parquet to Azure when **Storage Type** is Azure on the Amazon S3 connector. Azure supports Connection String, Shared Key, and SAS Token authentication. +--- + ## Prerequisites -Azure prerequisites will be added here. +- **ADLS Gen2:** Only hierarchical-namespace accounts are supported. +- The identity must be able to list, read, write, and delete blobs in the container. --- @@ -136,8 +139,8 @@ To use **Connection String** authentication, copy a storage account access key i | Parameter | Sample Value | Description | |-----------|--------------|-------------| | **storage_type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | -| **azure_auth_type**
`required` | `Connection String` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | -| **azure_container_name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | +| **azure_auth_type**
`required` | `connection-string` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **azure_container_name**
`required` | `olakeauthtest` | Name of an existing Azure storage container with permission to store output files. | | **azure_path** | `/data` | Path inside the container where data files are written. | | **azure_endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | | **azure_connection_string**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | One string that contains the account name, the access key, and the endpoint suffix. OLake Go pulls the key out of this string and signs each Blob request with it. | @@ -197,9 +200,9 @@ To use **Shared Key** authentication, copy a storage account access key in Azure | Parameter | Sample Value | Description | |-----------|--------------|-------------| | **storage_type** | `Azure` | Storage backend OLake Go uses to write Parquet files. Set to Azure to write to Azure Blob Storage. | -| **azure_auth_type**
`required` | `Shared Key` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | -| **azure_storage_account_name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | -| **azure_container_name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | +| **azure_auth_type**
`required` | `shared-key` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | +| **azure_storage_account_name**
`required` | `olakeadlsauthtest` | Name of the Azure storage account that contains the container. | +| **azure_container_name**
`required` | `warehouse` | Name of an existing Azure storage container with permission to store output files. | | **azure_path** | `/data` | Path inside the container where data files are written. | | **azure_endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | | **azure_storage_account_key**
`required` | `` | Raw storage account access key, passed separately from the account name. OLake Go uses the key to sign each Blob request. | From 9c23bbac4309aa92e9eb6efe1cd5c7f0980da46b Mon Sep 17 00:00:00 2001 From: Nayan Joshi Date: Mon, 26 Oct 2026 19:30:43 +0530 Subject: [PATCH 4/4] update based on review --- docs/writers/parquet/config.mdx | 50 +++++++++++++--------- static/img/docs/s3/azure-conn-string.webp | Bin 12842 -> 11708 bytes 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/docs/writers/parquet/config.mdx b/docs/writers/parquet/config.mdx index a2497d398..45981d096 100644 --- a/docs/writers/parquet/config.mdx +++ b/docs/writers/parquet/config.mdx @@ -78,7 +78,6 @@ OLake Go writes Parquet to Azure when **Storage Type** is Azure on the Amazon S3 ## Prerequisites -- **ADLS Gen2:** Only hierarchical-namespace accounts are supported. - The identity must be able to list, read, write, and delete blobs in the container. --- @@ -95,7 +94,14 @@ It supports multiple authentication types. Select the authentication mechanism y -To use **Connection String** authentication, copy a storage account access key in Azure and pass it to OLake Go inside one connection string. The string contains the account name, the key, and the endpoint suffix. OLake Go pulls the key out of the string and signs each Blob request with it. Follow the steps in the [Manage storage account access keys](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal) documentation to access the key. +To use **Connection String** authentication, pass one connection string to OLake Go. The key can be an account key or a SAS key. + +- The string must contain `AccountName` or `BlobEndpoint`, and `AccountKey` or `SharedAccessSignature`. +- When the string contains an `AccountKey`, OLake Go pulls that key out and signs each Blob request with it. +- A string with `SharedAccessSignature` is not signed with an `AccountKey`. +- `UseDevelopmentStorage=true` is rejected unless `AccountName`, `AccountKey`, and `BlobEndpoint` are all set. + +Follow the steps in the [Manage storage account access keys](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal) documentation to access the key. @@ -111,9 +117,8 @@ To use **Connection String** authentication, copy a storage account access key i | **Azure Auth Type**
`required` | `Connection String` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | | **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | | **Azure Path** | `/data` | Path inside the container where data files are written. | -| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | -| **Azure Connection String**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | One string that contains the account name, the access key, and the endpoint suffix. OLake Go pulls the key out of this string and signs each Blob request with it. | -| **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | +| **Azure Connection String**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | Connection string OLake Go uses to authenticate Blob requests. The key inside it can be an Account key or a SAS key. | +| **Max File Size (MB)** | A positive number (integers e.g. `512`, `128` or fractional e.g. `0.1` for `100KB`) | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to 512 when unset. |
@@ -127,7 +132,6 @@ To use **Connection String** authentication, copy a storage account access key i "azure_auth_type": "connection-string", "azure_container_name": "olakeauthtest", "azure_path": "/data", - "azure_endpoint": "https://.blob.core.windows.net/", "azure_connection_string": "", "max_file_size_mb": "" } @@ -142,9 +146,8 @@ To use **Connection String** authentication, copy a storage account access key i | **azure_auth_type**
`required` | `connection-string` | Authentication method OLake Go uses for Azure Blob requests. Set to match your storage account's authentication requirement. | | **azure_container_name**
`required` | `olakeauthtest` | Name of an existing Azure storage container with permission to store output files. | | **azure_path** | `/data` | Path inside the container where data files are written. | -| **azure_endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | -| **azure_connection_string**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | One string that contains the account name, the access key, and the endpoint suffix. OLake Go pulls the key out of this string and signs each Blob request with it. | -| **max_file_size_mb** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | +| **azure_connection_string**
`required` | `DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net` | Connection string OLake Go uses to authenticate Blob requests. The key inside it can be an Account key or a SAS key. | +| **max_file_size_mb** | A positive number (integers e.g. `512`, `128` or fractional e.g. `0.1` for `100KB`) | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to 512 when unset. |
@@ -171,9 +174,9 @@ To use **Shared Key** authentication, copy a storage account access key in Azure | **Azure Storage Account Name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | | **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container with permission to store output files. | | **Azure Path** | `/data` | Path inside the container where data files are written. | -| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`. Set it only when using a custom blob URL. | | **Azure Storage Account Key**
`required` | `` | Raw storage account access key, passed separately from the account name. OLake Go uses the key to sign each Blob request. | -| **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | +| **Max File Size (MB)** | A positive number (integers e.g. `512`, `128` or fractional e.g. `0.1` for `100KB`) | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to 512 when unset. | @@ -204,9 +207,9 @@ To use **Shared Key** authentication, copy a storage account access key in Azure | **azure_storage_account_name**
`required` | `olakeadlsauthtest` | Name of the Azure storage account that contains the container. | | **azure_container_name**
`required` | `warehouse` | Name of an existing Azure storage container with permission to store output files. | | **azure_path** | `/data` | Path inside the container where data files are written. | -| **azure_endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | +| **azure_endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`. Set it only when using a custom blob URL. | | **azure_storage_account_key**
`required` | `` | Raw storage account access key, passed separately from the account name. OLake Go uses the key to sign each Blob request. | -| **max_file_size_mb** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | +| **max_file_size_mb** | A positive number (integers e.g. `512`, `128` or fractional e.g. `0.1` for `100KB`) | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to 512 when unset. | @@ -216,7 +219,12 @@ To use **Shared Key** authentication, copy a storage account access key in Azure -To use **SAS Token** authentication, generate a shared access signature for your container in Azure and give OLake Go the token and the account name as separate fields. The token limits what OLake Go can do: it is scoped to a container, and its permissions and expiry can be set directly on the token or through a stored access policy. OLake Go appends the token to each Blob request, and Azure checks the signature and the permissions before allowing the request. Follow the steps in the [Grant limited access to Azure Storage](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview) resources using shared access signatures (SAS) documentation to create the token. +To use **SAS Token** authentication, generate a shared access signature for your container in Azure and give OLake Go the token and the account name as separate fields. The token limits what OLake Go can do: it is scoped to a container, and its permissions can be set directly on the token or through a stored access policy. OLake Go appends the token to each Blob request, and Azure checks the signature and the permissions before allowing the request. + +- The token must be the query string (`sv=...&sig=...`), with or without a leading `?`. It must not be the full blob URL. +- The expiry is whatever was set when the SAS was created. + +Follow the steps in the [Grant limited access to Azure Storage](https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview) resources using shared access signatures (SAS) documentation to create the token. @@ -233,9 +241,9 @@ To use **SAS Token** authentication, generate a shared access signature for your | **Azure Storage Account Name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | | **Azure Storage Container Name**
`required` | `olake-data` | Name of an existing Azure storage container the SAS token is scoped to. The container must already exist and the token must allow writes. | | **Azure Path** | `/data` | Path inside the container where data files are written. | -| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | -| **Azure SAS Token**
`required` | `` | Shared access signature for the container, passed separately from the account name. OLake Go appends this token to each Blob request. | -| **Max File Size (MB)** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | +| **Azure Endpoint** | `https://.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`. Set it only when using a custom blob URL. | +| **Azure SAS Token**
`required` | `sv=2024-11-04&sr=c&sig=` | SAS query string OLake Go appends to each Blob request. Pass `sv=...&sig=...`, not the full blob URL. | +| **Max File Size (MB)** | A positive number (integers e.g. `512`, `128` or fractional e.g. `0.1` for `100KB`) | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to 512 when unset. |
@@ -250,7 +258,7 @@ To use **SAS Token** authentication, generate a shared access signature for your "azure_storage_account_name": "olakedata", "azure_endpoint": "https://olakedata.blob.core.windows.net/", "azure_path": "/data", - "azure_sas_token": "spr=https&sv=2024-11-04&si=olake-rw&sr=c&sig=", + "azure_sas_token": "sv=2024-11-04&sr=c&sig=", "azure_container_name": "warehouse", "max_file_size_mb": 512 } @@ -266,9 +274,9 @@ To use **SAS Token** authentication, generate a shared access signature for your | **azure_storage_account_name**
`required` | `olakedata` | Name of the Azure storage account that contains the container. | | **azure_container_name**
`required` | `warehouse` | Name of an existing Azure storage container the SAS token is scoped to. The container must already exist and the token must allow writes. | | **azure_path** | `/data` | Path inside the container where data files are written. | -| **azure_endpoint** | `https://olakedata.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`.

For Azurite, use `http://:/`. | -| **azure_sas_token**
`required` | `spr=https&sv=2024-11-04&si=olake-rw&sr=c&sig=` | Shared access signature for the container, passed separately from the account name. OLake Go appends this token to each Blob request. | -| **max_file_size_mb** | `512` | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to `512` when unset. | +| **azure_endpoint** | `https://olakedata.blob.core.windows.net/` | Blob service URL. Leave empty to use `https://{account}.blob.core.windows.net/`. Set it only when using a custom blob URL. | +| **azure_sas_token**
`required` | `sv=2024-11-04&sr=c&sig=` | SAS query string OLake Go appends to each Blob request. Pass `sv=...&sig=...`, not the full blob URL. | +| **max_file_size_mb** | A positive number (integers e.g. `512`, `128` or fractional e.g. `0.1` for `100KB`) | Rolls a partition into a new parquet file once the current file reaches this size in MB. Fractional values are allowed. Defaults to 512 when unset. | diff --git a/static/img/docs/s3/azure-conn-string.webp b/static/img/docs/s3/azure-conn-string.webp index 39d249631f1c004af58a3284e407d956636077c4..08f4cbbbf60bb9b40afd04db4e36f6080d7bd85d 100644 GIT binary patch literal 11708 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