Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8fbea5e
start by moving auth config to azure_common
jlaundry Feb 22, 2026
cf02890
add auth to azure_blob
jlaundry Feb 24, 2026
f560854
initial auth switch
jlaundry Feb 25, 2026
2903a28
integration test WIP
jlaundry Feb 25, 2026
45d9e74
doc updates
jlaundry Feb 25, 2026
206692f
add feature changelog
jlaundry Feb 25, 2026
30e73b5
clippy changes
jlaundry Feb 25, 2026
5a0b5d2
add account_name and blob_endpoint options
jlaundry Feb 27, 2026
c611d4d
remove block_in_place
jlaundry Feb 28, 2026
1ad3097
add ClientCertificateCredential
jlaundry Mar 4, 2026
d03f6a6
remake certificate with required attributes
jlaundry Mar 4, 2026
f3dc448
refactor integration tests to support both connection string and oauth
jlaundry Mar 5, 2026
2de30d8
update doc examples
jlaundry Mar 5, 2026
6f94501
make clippy happy
jlaundry Mar 5, 2026
50e4ff0
fix tests
jlaundry Mar 5, 2026
bc44aea
make "client_secret_credential" explicitly configured
jlaundry Mar 10, 2026
69b8b38
add azure_logs_ingestion to Azure integration tests
jlaundry Mar 10, 2026
f2ec30f
Err out the other TlsConfig options
jlaundry Mar 10, 2026
212a90a
Add warning message to connection_string
jlaundry Mar 10, 2026
4ed4324
Simplify TlsConfig
jlaundry Mar 10, 2026
e4b650f
add workload identity config options
jlaundry Mar 10, 2026
17d51bb
Merge remote-tracking branch 'origin/master' into feature-azure_auth
thomasqueirozb Mar 12, 2026
5af8d1a
Move warning into docs::warnings
thomasqueirozb Mar 12, 2026
348cc24
Properly regenerate cue file
thomasqueirozb Mar 12, 2026
9254aee
Fix spaces
thomasqueirozb Mar 12, 2026
a18adfc
tidy options creation
jlaundry Mar 12, 2026
2d9f1a6
add UAMI type option
jlaundry Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actions/spelling/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ mycie
mycorp
mydatabase
mylabel
mylogstorage
mypod
myvalue
Namazu
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ aws-smithy-types = { version = "1.2.11", default-features = false, features = ["

# Azure
azure_core = { version = "0.30", features = ["reqwest", "hmac_openssl"], optional = true }
azure_identity = { version = "0.30", optional = true }
azure_identity = { version = "0.30", features = ["client_certificate"], optional = true }

# Azure Storage
azure_storage_blob = { version = "0.7", optional = true }
Expand Down Expand Up @@ -868,7 +868,7 @@ sinks-aws_s3 = ["dep:base64", "dep:md-5", "aws-core", "dep:aws-sdk-s3"]
sinks-aws_sqs = ["aws-core", "dep:aws-sdk-sqs"]
sinks-aws_sns = ["aws-core", "dep:aws-sdk-sns"]
sinks-axiom = ["sinks-http"]
sinks-azure_blob = ["dep:azure_core", "dep:azure_storage_blob"]
sinks-azure_blob = ["dep:azure_core", "dep:azure_identity", "dep:azure_storage_blob"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might consider updating to the current (february) Azure SDK. The only iffy part of that is that it brings a dependency on reqwest version 13, not 12.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right in thinking that 0.33 is imminent? I was thinking of waiting for Azure/azure-sdk-for-rust#3643 and Azure/azure-sdk-for-rust#3807

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.33 should be out in a day or so - the big change in 0.33 is removing support for wasm, the rest are relatively minor changes.

However I don't know what the timeline is for the corresponding storage releases. And until we GA the azure SDK, all the SDK client packages are tied at the hip - they all need to be re-released. After GA, we will have a somewhat looser coupling between azure_core and the other crates.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it turns out I was mistaken. azure_core 0.33 is out now. So it's available but the corresponding storage is not quite.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like azure_storage_blob@0.10.0 just dropped

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, sorry I forgot to mention it to you - yesterday was a bit chaotic (for the storage team as well - they had to make some last minute changes to make the March release).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries 🙂

I've pushed the change on a separate branch (jlaundry/vector@feature-azure_auth...jlaundry:vector:update-azure-crates-0.33), and the update caused the integration tests to fail:

"400: <?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Error>\n <Code>InvalidHeaderValue</Code>\n <Message>The API version 2026-04-06 is not supported by Azurite. Please upgrade Azurite to latest version and retry. If you are using Azurite in Visual Studio, please check you have installed latest Visual Studio patch. Azurite command line parameter \"--skipApiVersionCheck\" or Visual Studio Code configuration \"Skip Api Version Check\" can skip this error. \nRequestId:70cc1270-db0c-4e13-ba15-41f3eca1352e\nTime:2026-03-12T20:02:13.682Z</Message>\n</Error>

Related: Azure/Azurite#2623

Adding --skipApiVersionCheck to the docker-compose file works (at least, the tests pass...)

@thomasqueirozb are you happy if I pull these updates into this PR, or would you prefer to have a separate one right after?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, Azurite has a --ignore-api-version switch (or something like that - I don't remember what exactly it is) which I ended up having to use in my testing.

sinks-azure_logs_ingestion = ["dep:azure_core", "dep:azure_identity"]
sinks-azure_monitor_logs = []
sinks-blackhole = []
Expand Down Expand Up @@ -979,7 +979,8 @@ aws-integration-tests = [
]

azure-integration-tests = [
"azure-blob-integration-tests"
"azure-blob-integration-tests",
"azure-logs-ingestion-integration-tests",
]

aws-cloudwatch-logs-integration-tests = ["sinks-aws_cloudwatch_logs"]
Expand All @@ -993,6 +994,7 @@ aws-sqs-integration-tests = ["sinks-aws_sqs"]
aws-sns-integration-tests = ["sinks-aws_sns"]
axiom-integration-tests = ["sinks-axiom"]
azure-blob-integration-tests = ["sinks-azure_blob"]
azure-logs-ingestion-integration-tests = ["sinks-azure_logs_ingestion"]
chronicle-integration-tests = ["sinks-gcp"]
clickhouse-integration-tests = ["sinks-clickhouse"]
databend-integration-tests = ["sinks-databend"]
Expand Down
3 changes: 3 additions & 0 deletions changelog.d/24729_azure_blob_authentication.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Re-introduced Azure authentication support to `azure_blob`, including Azure CLI, Managed Identity, Workload Identity, and Managed Identity-based Client Assertion authentication types.

authors: jlaundry
78 changes: 73 additions & 5 deletions src/sinks/azure_blob/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use crate::{
sinks::{
Healthcheck, VectorSink,
azure_common::{
self, config::AzureBlobRetryLogic, service::AzureBlobService, sink::AzureBlobSink,
self, config::AzureAuthentication, config::AzureBlobRetryLogic,
config::AzureBlobTlsConfig, service::AzureBlobService, sink::AzureBlobSink,
},
util::{
BatchConfig, BulkSizeBasedDefaultBatchSettings, Compression, ServiceBuilderExt,
Expand All @@ -41,6 +42,10 @@ impl TowerRequestConfigDefaults for AzureBlobTowerRequestConfigDefaults {
#[derive(Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct AzureBlobSinkConfig {
#[configurable(derived)]
#[serde(default)]
pub auth: Option<AzureAuthentication>,

/// The Azure Blob Storage Account connection string.
///
/// Authentication with an access key or shared access signature (SAS)
Expand All @@ -55,13 +60,33 @@ pub struct AzureBlobSinkConfig {
/// | Allowed services | Blob |
/// | Allowed resource types | Container & Object |
/// | Allowed permissions | Read & Create |
#[configurable(metadata(
docs::warnings = "Access keys and SAS tokens can be used to gain unauthorized access to Azure Blob Storage \
resources. Numerous security breaches have occurred due to leaked connection strings. It is important to keep \
connection strings secure and not expose them in logs, error messages, or version control systems."
))]
#[configurable(metadata(
docs::examples = "DefaultEndpointsProtocol=https;AccountName=mylogstorage;AccountKey=storageaccountkeybase64encoded;EndpointSuffix=core.windows.net"
))]
#[configurable(metadata(
docs::examples = "BlobEndpoint=https://mylogstorage.blob.core.windows.net/;SharedAccessSignature=generatedsastoken"
))]
pub connection_string: SensitiveString,
#[configurable(metadata(docs::examples = "AccountName=mylogstorage"))]
pub connection_string: Option<SensitiveString>,

/// The Azure Blob Storage Account name.
///
/// If provided, this will be used instead of the `connection_string`.
/// This is useful for authenticating with an Azure credential.
#[configurable(metadata(docs::examples = "mylogstorage"))]
pub(super) account_name: Option<String>,

/// The Azure Blob Storage endpoint.
///
/// If provided, this will be used instead of the `connection_string`.
/// This is useful for authenticating with an Azure credential.
#[configurable(metadata(docs::examples = "https://mylogstorage.blob.core.windows.net/"))]
pub(super) blob_endpoint: Option<String>,

/// The Azure Blob Storage Account container name.
#[configurable(metadata(docs::examples = "my-logs"))]
Expand Down Expand Up @@ -138,6 +163,10 @@ pub struct AzureBlobSinkConfig {
skip_serializing_if = "crate::serde::is_default"
)]
pub(super) acknowledgements: AcknowledgementsConfig,

#[configurable(derived)]
#[serde(default)]
pub tls: Option<AzureBlobTlsConfig>,
}

pub fn default_blob_prefix() -> Template {
Expand All @@ -147,7 +176,10 @@ pub fn default_blob_prefix() -> Template {
impl GenerateConfig for AzureBlobSinkConfig {
fn generate_config() -> toml::Value {
toml::Value::try_from(Self {
connection_string: String::from("DefaultEndpointsProtocol=https;AccountName=some-account-name;AccountKey=some-account-key;").into(),
auth: None,
connection_string: Some(String::from("DefaultEndpointsProtocol=https;AccountName=some-account-name;AccountKey=some-account-key;").into()),
account_name: None,
blob_endpoint: None,
container_name: String::from("logs"),
blob_prefix: default_blob_prefix(),
blob_time_format: Some(String::from("%s")),
Expand All @@ -157,6 +189,7 @@ impl GenerateConfig for AzureBlobSinkConfig {
batch: BatchConfig::default(),
request: TowerRequestConfig::default(),
acknowledgements: Default::default(),
tls: None,
})
.unwrap()
}
Expand All @@ -166,11 +199,46 @@ impl GenerateConfig for AzureBlobSinkConfig {
#[typetag::serde(name = "azure_blob")]
impl SinkConfig for AzureBlobSinkConfig {
async fn build(&self, cx: SinkContext) -> Result<(VectorSink, Healthcheck)> {
let connection_string: String = match (
&self.connection_string,
&self.account_name,
&self.blob_endpoint,
) {
(Some(connstr), None, None) => connstr.inner().into(),
(None, Some(account_name), None) => {
format!("AccountName={}", account_name)
}
(None, None, Some(blob_endpoint)) => {
// BlobEndpoint must always end in a trailing slash
let blob_endpoint = if blob_endpoint.ends_with('/') {
blob_endpoint.clone()
} else {
format!("{}/", blob_endpoint)
};
format!("BlobEndpoint={}", blob_endpoint)
}
(None, None, None) => {
return Err("One of `connection_string`, `account_name`, or `blob_endpoint` must be provided".into());
}
(Some(_), Some(_), _) => {
return Err("Cannot provide both `connection_string` and `account_name`".into());
}
(Some(_), _, Some(_)) => {
return Err("Cannot provide both `connection_string` and `blob_endpoint`".into());
}
(_, Some(_), Some(_)) => {
return Err("Cannot provide both `account_name` and `blob_endpoint`".into());
}
};

let client = azure_common::config::build_client(
self.connection_string.clone().into(),
self.auth.clone(),
connection_string.clone(),
self.container_name.clone(),
cx.proxy(),
)?;
self.tls.clone(),
)
.await?;

let healthcheck = azure_common::config::build_healthcheck(
self.container_name.clone(),
Expand Down
Loading
Loading