<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Observability Labs - Articles by Álex Cámara</title>
        <link>https://www.elastic.co/observability-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Mon, 06 Apr 2026 17:03:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Observability Labs - Articles by Álex Cámara</title>
            <url>https://www.elastic.co/observability-labs/assets/observability-labs-thumbnail.png</url>
            <link>https://www.elastic.co/observability-labs</link>
        </image>
        <copyright>© 2026. Elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[Migrate Logstash Pipelines from Azure Event Hubs to Kafka Input Plugin]]></title>
            <link>https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin</link>
            <guid isPermaLink="false">migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin</guid>
            <pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Step-by-step guide to migrating Logstash pipelines from the Azure Event Hubs plugin to the Kafka input plugin to eliminate offset storage costs and improve performance.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Azure Event Hubs natively supports the Apache Kafka protocol, which means you no longer need the <code>logstash-input-azure_event_hubs</code> plugin or an external Blob Storage account for offset checkpointing. Switching to <code>logstash-input-kafka</code> removes that storage dependency, reduces costs, and delivers up to 2.5x higher throughput.</p>
<p>This guide walks you through the migration: why it matters, how to convert your existing configuration, parameter mapping between the two plugins, and how to adapt proxy setups.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin/amqp-vs-kafka.png" alt="AMQP vs Kafka protocol path comparison for Logstash with Azure Event Hubs" /></p>
<h2>Why migrate?</h2>
<p>The migration from the Azure Event Hubs plugin to the Kafka input plugin is motivated by several factors:</p>
<ol>
<li>
<p><strong>Azure Event Hubs already speaks Kafka natively.</strong> Event Hubs exposes a <a href="https://learn.microsoft.com/en-us/azure/event-hubs/azure-event-hubs-kafka-overview">built-in Apache Kafka endpoint</a> on Standard, Premium, and Dedicated tiers. This means the <code>logstash-input-azure_event_hubs</code> plugin is no longer necessary. The standard <code>logstash-integration-kafka</code> (input) plugin connects directly to the same service with no extra Azure-side configuration.</p>
</li>
<li>
<p><strong>No more Blob Storage for offset checkpointing.</strong> The AMQP-based plugin requires an <a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-processor-balance-partition-load#checkpoint">external Azure Blob Storage account</a> to track consumer offsets. This means provisioning and maintaining a storage account, plus paying for every checkpoint write. With the Kafka protocol, <a href="https://learn.microsoft.com/en-us/azure/event-hubs/apache-kafka-frequently-asked-questions#event-hubs-consumer-group-vs--kafka-consumer-group">offset tracking is handled internally by Azure Event Hubs at no extra cost</a>, removing the need for external storage.</p>
</li>
<li>
<p><strong>GPv1 storage retirement is coming, and GPv2 costs more.</strong> Microsoft will <a href="https://learn.microsoft.com/en-us/azure/storage/common/general-purpose-version-1-account-migration-overview">retire general-purpose v1 storage accounts in October 2026</a>. Accounts not manually <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-upgrade">upgraded to GPv2</a> by then will be migrated automatically. The <code>logstash-input-azure_event_hubs</code> plugin works correctly with GPv2, so existing pipelines will not break. However, GPv2 can bring <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-upgrade#billing-impact-of-upgrading">higher transactional costs</a>, especially for checkpoint-heavy workloads. By switching to the Kafka input plugin, this concern is eliminated: no storage account means nothing to upgrade and nothing to pay for.</p>
<p><strong>Not ready to migrate yet? Reducing GPv2 costs in the meantime is possible.</strong> GPv2 transaction pricing is significantly more expensive than GPv1's flat rate. Increasing the <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-checkpoint_interval"><code>checkpoint_interval</code></a> setting above its default of 5 seconds reduces write operations and lowers the cost impact. The cost difference can be estimated using the <a href="https://azure.microsoft.com/en-us/pricing/calculator/">Azure Pricing Calculator</a>.</p>
<p>Example for East US and Local Retention Storage. Write operation cost comparison (per 10,000 write operations):</p>
<ul>
<li>
<p><strong>GPv1 (flat):</strong> $0.00036</p>
</li>
<li>
<p><strong>GPv2 (Hot tier):</strong> $0.050</p>
</li>
</ul>
<p>That's roughly a 140x increase in write operation costs.</p>
</li>
<li>
<p><strong>Broader community and active maintenance.</strong> The Kafka input plugin is more widely used across Logstash deployments and receives regular updates aligned with the Kafka ecosystem. Moving to it reduces long-term operational risk and keeps your pipeline on a well-supported path.</p>
</li>
<li>
<p><strong>Better throughput.</strong> The Kafka input plugin consistently outperforms the Azure Event Hubs plugin when consuming from the same namespace. See the <a href="#performance-comparison">Performance Comparison</a> section for measured results.</p>
</li>
</ol>
<h2>Requirements to enable the Kafka interface</h2>
<p>The Kafka interface is built into Azure Event Hubs. You don't need to enable or configure anything in the Azure portal.</p>
<p>The only requirement is that your Event Hubs namespace is on the <strong>Standard</strong>, <strong>Premium</strong>, or <strong>Dedicated</strong> tier. The Basic tier does not support the Kafka protocol.</p>
<p>See the <a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas#basic-vs-standard-vs-premium-vs-dedicated-tiers">Tiers comparison table</a> for details.</p>
<h2>Converting your configuration</h2>
<p>This section walks through converting an existing <code>logstash-input-azure_event_hubs</code> configuration to <code>logstash-input-kafka</code>, starting with the simplest single-hub scenario and building up to multi-hub and advanced use cases.</p>
<h3>Key behavior changes</h3>
<p>Before changing any configuration, be aware of two important differences:</p>
<ol>
<li>
<p><strong>No more Blob Storage for offsets.</strong> The Kafka input plugin tracks offsets internally through the Azure Event Hubs service at no extra cost. The <code>storage_connection</code> and <code>storage_container</code> parameters have no equivalent. There is nothing to provision, maintain, or pay for.</p>
</li>
<li>
<p><strong>Consumer offsets don't carry over.</strong> AMQP consumer groups and Kafka consumer groups are completely separate, even if they share the same name. When the Kafka input plugin connects for the first time, Azure auto-creates the Kafka consumer group specified in <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-group_id"><code>group_id</code></a> (default: <code>logstash</code>). <strong>It will not read the old Blob Storage checkpoints or resume from where the legacy plugin left off.</strong> It starts fresh.</p>
</li>
</ol>
<table>
<thead>
<tr>
<th></th>
<th>Event Hubs (AMQP) consumer groups</th>
<th>Kafka consumer groups</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Protocol</strong></td>
<td>AMQP</td>
<td>Kafka</td>
</tr>
<tr>
<td><strong>Offset storage</strong></td>
<td>External Azure Blob Storage</td>
<td>Internal to the Event Hubs service</td>
</tr>
<tr>
<td><strong>Creation</strong></td>
<td>Must be created via portal, SDK, or ARM</td>
<td>Auto-created on first connection</td>
</tr>
<tr>
<td><strong>Namespace scope</strong></td>
<td>Scoped to a single Event Hub</td>
<td>Span the entire namespace</td>
</tr>
</tbody>
</table>
<p><strong>Limit:</strong> A maximum of 1,000 simultaneous Kafka consumer groups per namespace is allowed. See the <a href="https://learn.microsoft.com/en-us/azure/event-hubs/apache-kafka-frequently-asked-questions#event-hubs-consumer-group-vs--kafka-consumer-group">Event Hubs vs. Kafka Consumer Groups FAQ</a>.</p>
<h3>Authentication</h3>
<p>The <code>logstash-input-azure_event_hubs</code> plugin only supports <strong>SAS (Shared Access Signature)</strong> authentication via connection strings. The same SAS credentials work with the Kafka plugin through SASL PLAIN, as shown in the <a href="#single-event-hub-basic-migration">Single Event Hub (basic migration)</a> example below.</p>
<h3>Single Event Hub (basic migration)</h3>
<p>Most pipelines start with a single Event Hub, SAS authentication, and Blob Storage checkpointing. The following example shows the baseline <code>azure_event_hubs</code> configuration and its direct Kafka equivalent.</p>
<p><strong>Before</strong> (legacy Azure Event Hubs input):</p>
<pre><code class="language-ruby">input {
  azure_event_hubs {
    event_hub_connections =&gt; [&quot;Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;ACCESS_KEY_NAME&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;;EntityPath=&lt;EVENT_HUB_NAME&gt;&quot;]
    storage_connection =&gt; &quot;DefaultEndpointsProtocol=https;AccountName=&lt;STORAGE_ACCOUNT_NAME&gt;;AccountKey=&lt;STORAGE_ACCOUNT_KEY&gt;;EndpointSuffix=core.windows.net&quot;
    consumer_group =&gt; &quot;&lt;CONSUMER_GROUP_NAME&gt;&quot;
    storage_container =&gt; &quot;&lt;STORAGE_NAME&gt;&quot;
  }
}
</code></pre>
<p><strong>After</strong> (Kafka input):</p>
<pre><code class="language-ruby">input {
  kafka {
    # The Namespace name and the mandatory Kafka SSL port
    bootstrap_servers =&gt; &quot;&lt;NAMESPACE&gt;.servicebus.windows.net:9093&quot;
    
    topics =&gt; [&quot;&lt;EVENT_HUB_NAME&gt;&quot;]
    group_id =&gt; &quot;&lt;KAFKA_CONSUMER_GROUP_NAME&gt;&quot;
    security_protocol =&gt; &quot;SASL_SSL&quot;
    sasl_mechanism =&gt; &quot;PLAIN&quot;
    
    # Need to create a 'jaas.conf' file storing Username and Password (username is always '$ConnectionString')
    jaas_path =&gt; &quot;path/to/jaas.conf&quot;
  }
}
</code></pre>
<pre><code class="language-text">KafkaClient {
    org.apache.kafka.common.security.plain.PlainLoginModule required
    username=&quot;$ConnectionString&quot; 
    password=&quot;Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;ACCESS_KEY_NAME&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;&quot;;
};
</code></pre>
<pre><code class="language-ruby"># Inline JAAS configuration (substitutes jaas_path)
    sasl_jaas_config =&gt; &quot;org.apache.kafka.common.security.plain.PlainLoginModule required username='$ConnectionString' password='Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;ACCESS_KEY_NAME&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;';&quot;
</code></pre>
<h3>Multiple Event Hubs with a single Kafka input</h3>
<p>If your SAS policy has <strong>namespace-level read rights</strong> (not just a single Event Hub), you can consume from multiple Event Hubs with a single <code>kafka</code> input by listing multiple topics:</p>
<pre><code class="language-ruby">input {
  kafka {
    bootstrap_servers =&gt; &quot;&lt;NAMESPACE&gt;.servicebus.windows.net:9093&quot;
    topics =&gt; [&quot;&lt;EVENT_HUB_1&gt;&quot;, &quot;&lt;EVENT_HUB_2&gt;&quot;, &quot;&lt;EVENT_HUB_3&gt;&quot;]
    group_id =&gt; &quot;&lt;KAFKA_CONSUMER_GROUP_NAME&gt;&quot;
    security_protocol =&gt; &quot;SASL_SSL&quot;
    sasl_mechanism =&gt; &quot;PLAIN&quot;
    jaas_path =&gt; &quot;path/to/jaas.conf&quot;
  }
}
</code></pre>
<h2>Configuration parameters mapping</h2>
<p>The following section maps each <code>logstash-input-azure_event_hubs</code> parameter to its <code>logstash-input-kafka</code> equivalent, with usage notes and example configurations.</p>
<ol>
<li>
<p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-config_mode"><code>config_mode</code></a>: No direct equivalent. Kafka doesn't have &quot;basic&quot; vs &quot;advanced&quot; modes. To consume from multiple hubs with different settings, define multiple <code>kafka {}</code> input blocks or list multiple topics. The basic mode conversion is covered in <a href="#single-event-hub-basic-migration">Single Event Hub (basic migration)</a>.</p>
<p>Here is an advanced-mode example with two Event Hubs in the same namespace:</p>
<pre><code class="language-ruby">input {
    azure_event_hubs {
        config_mode =&gt; &quot;advanced&quot;
        storage_connection =&gt; &quot;DefaultEndpointsProtocol=https;AccountName=&lt;STORAGE_ACCOUNT&gt;;...&quot;
        event_hubs =&gt; [
            {&quot;&lt;EVENT_HUB_1&gt;&quot; =&gt; {
                event_hub_connection =&gt; &quot;Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;KEY_1&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;;EntityPath=&lt;EVENT_HUB_1&gt;&quot;
                consumer_group =&gt; &quot;&lt;CONSUMER_GROUP_1&gt;&quot;
            }},
            {&quot;&lt;EVENT_HUB_2&gt;&quot; =&gt; {
                event_hub_connection =&gt; &quot;Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;KEY_2&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;;EntityPath=&lt;EVENT_HUB_2&gt;&quot;
                consumer_group =&gt; &quot;&lt;CONSUMER_GROUP_2&gt;&quot;
            }}
        ]
    }
}
</code></pre>
<pre><code class="language-ruby">input {
    kafka {
        bootstrap_servers =&gt; &quot;&lt;NAMESPACE&gt;.servicebus.windows.net:9093&quot;
        topics =&gt; [&quot;&lt;EVENT_HUB_1&gt;&quot;]
        group_id =&gt; &quot;&lt;KAFKA_CONSUMER_GROUP_1&gt;&quot;
        security_protocol =&gt; &quot;SASL_SSL&quot;
        sasl_mechanism =&gt; &quot;PLAIN&quot;
        sasl_jaas_config =&gt; &quot;...&lt;KEY_1&gt;...&quot;
    }
    kafka {
        bootstrap_servers =&gt; &quot;&lt;NAMESPACE&gt;.servicebus.windows.net:9093&quot;
        topics =&gt; [&quot;&lt;EVENT_HUB_2&gt;&quot;]
        group_id =&gt; &quot;&lt;KAFKA_CONSUMER_GROUP_2&gt;&quot;
        security_protocol =&gt; &quot;SASL_SSL&quot;
        sasl_mechanism =&gt; &quot;PLAIN&quot;
        sasl_jaas_config =&gt; &quot;...&lt;KEY_2&gt;...&quot;
    }
}
</code></pre>
</li>
<li>
<p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-checkpoint_interval"><code>checkpoint_interval</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-auto_commit_interval_ms"><code>auto_commit_interval_ms</code></a>.</p>
<p>In the Azure plugin, this controls how often a write operation hits the Blob Storage container to save the reading offset. In the Kafka plugin, it controls how often the consumer commits its offset to the Event Hubs service.</p>
<p><strong>Note</strong> Keep <code>enable_auto_commit</code> set to <code>true</code> (default) while configuring <code>auto_commit_interval_ms</code> parameter.</p>
<p>Azure config:</p>
<pre><code class="language-ruby">input {
    azure_event_hubs {
        # ... other params ...
        checkpoint_interval =&gt; 10 # in seconds
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code class="language-ruby">input {
    kafka {
        # ... other params ...
        auto_commit_interval_ms =&gt; 10000 # in milliseconds 
    }
}
</code></pre>
</li>
<li>
<p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-decorate_events"><code>decorate_events</code></a>: This parameter exists in both plugins with the same name and behavior.</p>
</li>
<li>
<p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-initial_position"><code>initial_position</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-auto_offset_reset"><code>auto_offset_reset</code></a>.</p>
<p>Both parameters control where to start reading when no prior offset is found at checkpoint storage. Options differ slightly:</p>
<ul>
<li>
<p>Azure: <code>beginning</code>, <code>end</code>, <code>look_back</code></p>
</li>
<li>
<p>Kafka: <code>earliest</code>, <code>latest</code>, <code>by_duration:&lt;duration&gt;</code>, <code>none</code></p>
</li>
</ul>
<p>The difference between beginning-end and earliest-latest is purely terminology.</p>
<p>Azure config:</p>
<pre><code class="language-ruby">input {
    azure_event_hubs {
        initial_position =&gt; &quot;beginning&quot;
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code class="language-ruby">input {
    kafka {
        auto_offset_reset =&gt; &quot;earliest&quot;
    }
}
</code></pre>
<table>
<thead>
<tr>
<th>Azure Value</th>
<th>Kafka Value</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>beginning</code></td>
<td><code>earliest</code></td>
<td></td>
</tr>
<tr>
<td><code>end</code></td>
<td><code>latest</code></td>
<td></td>
</tr>
<tr>
<td><code>look_back</code></td>
<td><code>by_duration:&lt;duration&gt;</code></td>
<td>Duration in ISO 8601 format (e.g., <code>by_duration:PT1H</code> for 1 hour). Requires <code>logstash-integration-kafka</code> 12.1.0+.</td>
</tr>
</tbody>
</table>
<p>The <code>by_duration</code> option was introduced in Apache Kafka client 4.0.0 and is available in <code>logstash-integration-kafka</code> version 12.1.0 and later. The version bundled with the latest Logstash release is older than 12.1.0, so a manual gem update is needed:</p>
<pre><code class="language-bash">&lt;LOGSTASH_HOME&gt;/bin/logstash-plugin install --version 12.1.0 logstash-integration-kafka
</code></pre>
<p>Replace <code>&lt;LOGSTASH_HOME&gt;</code> with the Logstash installation directory (e.g., <code>/usr/share/logstash</code> for DEB/RPM packages).</p>
<p><strong>Note:</strong> Since Kafka can't read the old Blob Storage checkpoints, it treats the migration as a first-time connection. To avoid reprocessing data the legacy plugin already handled, set <code>auto_offset_reset =&gt; &quot;latest&quot;</code> for the initial deployment.</p>
</li>
<li>
<p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-max_batch_size"><code>max_batch_size</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-max_poll_records"><code>max_poll_records</code></a>.</p>
<p>Both parameters define the maximum number of messages to fetch in a single poll/batch operation.</p>
<p>Azure config:</p>
<pre><code class="language-ruby">input {
    azure_event_hubs {
        max_batch_size =&gt; 125
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code class="language-ruby">input {
    kafka {
        max_poll_records =&gt; &quot;125&quot;
    }
}
</code></pre>
</li>
<li>
<p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-threads"><code>threads</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-consumer_threads"><code>consumer_threads</code></a>.</p>
<p>Both parameters control the number of threads used to consume messages concurrently. In Azure, the minimum is 2 (with 1 Event Hub + 1), while in Kafka the default is 1 thread.</p>
<p>Azure config:</p>
<pre><code class="language-ruby">input {
    azure_event_hubs {
        threads =&gt; 8
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code class="language-ruby">input {
    kafka {
        consumer_threads =&gt; 8
    }
}
</code></pre>
</li>
</ol>
<h2>Performance Comparison</h2>
<p>We tested both plugins under identical conditions: same Logstash instance, same Event Hub namespace, same number of partitions, and same batch/thread configuration. The absolute numbers are environment-specific, but the relative difference is what matters.</p>
<table>
<thead>
<tr>
<th><strong>Plugin</strong></th>
<th><strong>Payload</strong></th>
<th><strong>Throughput (events/s)</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>azure_event_hubs</code></td>
<td>100B</td>
<td>~5700</td>
</tr>
<tr>
<td><code>kafka</code></td>
<td>100B</td>
<td>~14500</td>
</tr>
<tr>
<td><code>azure_event_hubs</code></td>
<td>1KB</td>
<td>~1500</td>
</tr>
<tr>
<td><code>kafka</code></td>
<td>1KB</td>
<td>~3200</td>
</tr>
<tr>
<td><code>azure_event_hubs</code></td>
<td>10KB</td>
<td>~170</td>
</tr>
<tr>
<td><code>kafka</code></td>
<td>10KB</td>
<td>~290</td>
</tr>
</tbody>
</table>
<p>Across all payload sizes, the Kafka input plugin delivers 1.7x to 2.5x higher throughput. The gain is most noticeable with small payloads, where protocol overhead dominates. Beyond the infrastructure simplification (no Blob Storage, no GPv2 concerns), you also get a clear performance win.</p>
<h2>Proxy connection configuration</h2>
<blockquote>
<p>If the Logstash instance connects directly to Azure Event Hubs without a proxy, this section can be skipped.</p>
</blockquote>
<p>Proxy setups require special attention during this migration because the two plugins use fundamentally different protocols.</p>
<h3>Azure Event Hubs plugin setup (reference)</h3>
<p>The <code>logstash-input-azure_event_hubs</code> plugin supports HTTPS proxies. The setup involves:</p>
<ol>
<li>
<p>Set the proxy environment variable:</p>
<pre><code class="language-bash">export https_proxy=&quot;https://my_proxy:8080&quot;
</code></pre>
</li>
<li>
<p>Add the WebSockets transport flag to the Event Hubs connection string:</p>
<pre><code class="language-text">;TransportType=AmqpWebSockets
</code></pre>
</li>
<li>
<p>Add the following JVM options (Logstash <code>jvm.options</code>):</p>
<pre><code class="language-text">-Dhttp.proxyHost=my_proxy
-Dhttp.proxyPort=8080
-Dhttps.proxyHost=my_proxy
-Dhttps.proxyPort=8443
-Dhttp.nonProxyHosts=localhost|127.0.0.1
</code></pre>
</li>
</ol>
<h3>Migrating to a TCP (Layer 4) proxy</h3>
<p>The proxy setup from the Azure plugin is not compatible with the Kafka client. The Azure plugin communicates over AMQP/WebSockets (HTTP layer), which is why JVM proxy settings and <code>TransportType=AmqpWebSockets</code> work. The Kafka plugin opens a raw TCP socket to the broker. It never makes an HTTP request, so <strong>JVM HTTP proxy settings are ignored entirely</strong>. If the environment requires a proxy, the HTTP proxy needs to be replaced with a TCP (Layer 4) proxy.</p>
<h4>Step 1: Configure <code>/etc/hosts</code></h4>
<p>The Kafka client verifies that the TLS certificate matches the hostname in <code>bootstrap_servers</code>. Since the certificate is issued for <code>*.servicebus.windows.net</code>, <code>bootstrap_servers</code> must use the real Event Hubs FQDN, not the proxy address. A DNS override routes the FQDN to the proxy IP:</p>
<pre><code class="language-text"># /etc/hosts
&lt;PROXY_HOST_IP&gt;  &lt;NAMESPACE&gt;.servicebus.windows.net
</code></pre>
<h4>Step 2: Logstash configuration</h4>
<p>The Logstash configuration is identical to a non-proxied setup. The <code>/etc/hosts</code> override transparently routes traffic through the proxy, so <code>bootstrap_servers</code> still uses the Event Hubs FQDN:</p>
<pre><code class="language-ruby">input {
  kafka {
    bootstrap_servers =&gt; &quot;&lt;NAMESPACE&gt;.servicebus.windows.net:9093&quot;
    topics =&gt; [&quot;&lt;EVENT_HUB_NAME&gt;&quot;]
    security_protocol =&gt; &quot;SASL_SSL&quot;
    sasl_mechanism =&gt; &quot;PLAIN&quot;
    group_id =&gt; &quot;&lt;GROUP_ID&gt;&quot;
    jaas_path =&gt; &quot;&lt;PATH_TO_JAAS_FILE&gt;&quot;
  }
}
</code></pre>
<p>If the TCP proxy runs on the same host as Logstash or within a trusted network segment, the DNS override is not needed. Instead, point <code>bootstrap_servers</code> directly to the proxy IP (e.g., <code>&lt;PROXY_HOST_IP&gt;:9093</code>) and change <code>security_protocol</code> to <code>SASL_PLAINTEXT</code>. This delegates the TLS handshake to the proxy, while the link between Logstash and the proxy stays unencrypted. Only use this configuration when the Logstash-to-proxy path is secure.</p>
<pre><code class="language-ruby">input {
  kafka {
    bootstrap_servers =&gt; &quot;&lt;PROXY_HOST_IP&gt;:9093&quot;
    security_protocol =&gt; &quot;SASL_PLAINTEXT&quot;
  }
}
</code></pre>
<h2>Frequently asked questions</h2>
<p><strong>Are events lost when switching from the Azure Event Hubs plugin to the Kafka plugin?</strong></p>
<p>No. Events remain available within the configured retention period regardless of which protocol reads them. What changes is where the consumer starts reading. Since the Kafka plugin cannot access the AMQP plugin's Blob Storage checkpoints, it starts from scratch. Set <code>auto_offset_reset =&gt; &quot;earliest&quot;</code> to reprocess all retained events, or <code>auto_offset_reset =&gt; &quot;latest&quot;</code> to consume only new ones from the switchover point. See the <a href="#configuration-parameters-mapping"><code>initial_position</code> mapping</a> for details.</p>
<p><strong>What happens to the Azure Blob Storage account after migration?</strong></p>
<p>It is no longer needed for offset checkpointing. Once the Kafka plugin is confirmed to be consuming correctly and the <code>azure_event_hubs</code> input has been decommissioned, the storage account (or at least the checkpoint container) can be safely deleted. If the storage account is used for other purposes, only remove the specific container referenced in <code>storage_container</code>.</p>
<p><strong>Can the same consumer group name be reused?</strong></p>
<p>Yes, but it has no practical effect. AMQP and Kafka consumer groups are completely independent even if they share the same name. They use different protocols, different offset storage, and different scoping rules. Reusing the name will not cause the Kafka plugin to resume from the AMQP plugin's last checkpoint.</p>
<p><strong>Are other authentication methods supported?</strong></p>
<p>The <code>logstash-input-azure_event_hubs</code> plugin only supports SAS connection strings, so SAS is the only credential that needs to be carried over. There is no Entra ID, OAUTHBEARER, or managed identity configuration to migrate. The <code>logstash-input-kafka</code> plugin does support SASL OAUTHBEARER, so adopting token-based authentication becomes possible after migration.</p>
<p><strong>What if the proxy only allows traffic on port 443?</strong></p>
<p>The Kafka endpoint on Azure Event Hubs requires port 9093. If the TCP proxy only forwards port 443, it must be reconfigured to also allow port 9093 for the Event Hubs FQDN (<code>*.servicebus.windows.net</code>). Azure Event Hubs does not expose a Kafka listener on port 443.</p>
<h2>Next steps</h2>
<p>With the GPv1 retirement deadline (October 2026) approaching, starting this migration sooner reduces the time spent managing storage infrastructure that is no longer needed.</p>
<p>If any issues arise during migration:</p>
<ul>
<li>
<p><strong>Usage questions or help with configuration</strong>: Post on the <a href="https://discuss.elastic.co/c/logstash/">Elastic Discuss forum</a>.</p>
</li>
<li>
<p><strong>Bugs or unexpected behavior in the Kafka plugin</strong>: Open an issue in the <a href="https://github.com/logstash-plugins/logstash-integration-kafka/issues">logstash-integration-kafka</a>.</p>
</li>
</ul>
<h2>Related resources</h2>
<ul>
<li><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html">Kafka input plugin documentation</a>: Full reference for all <code>logstash-input-kafka</code> configuration parameters.</li>
<li><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html">Azure Event Hubs input plugin documentation</a>: Full reference for the legacy plugin being replaced.</li>
<li><a href="https://learn.microsoft.com/en-us/azure/event-hubs/azure-event-hubs-kafka-overview">Azure Event Hubs for Apache Kafka overview</a>: Microsoft's documentation on the built-in Kafka endpoint in Event Hubs.</li>
<li><a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas#basic-vs-standard-vs-premium-vs-dedicated-tiers">Event Hubs quotas and tier comparison</a>: Tier requirements for Kafka protocol support.</li>
</ul>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin/elastic-blog-logstash-kafka.jpeg" length="0" type="image/jpeg"/>
        </item>
    </channel>
</rss>