diff --git a/Testcontainers.slnx b/Testcontainers.slnx
index fd3853239..1f12420fe 100644
--- a/Testcontainers.slnx
+++ b/Testcontainers.slnx
@@ -37,6 +37,7 @@
+
@@ -106,6 +107,7 @@
+
diff --git a/src/Testcontainers.FlociAz/.editorconfig b/src/Testcontainers.FlociAz/.editorconfig
new file mode 100644
index 000000000..78b36ca08
--- /dev/null
+++ b/src/Testcontainers.FlociAz/.editorconfig
@@ -0,0 +1 @@
+root = true
diff --git a/src/Testcontainers.FlociAz/FlociAzBuilder.cs b/src/Testcontainers.FlociAz/FlociAzBuilder.cs
new file mode 100644
index 000000000..a7c5de18f
--- /dev/null
+++ b/src/Testcontainers.FlociAz/FlociAzBuilder.cs
@@ -0,0 +1,97 @@
+namespace Testcontainers.FlociAz;
+
+///
+[PublicAPI]
+public sealed class FlociAzBuilder : ContainerBuilder
+{
+ public const ushort FlociAzPort = 4577;
+
+ public const ushort EventHubsPort = 5672;
+
+ public const ushort ServiceBusPort = 5673;
+
+ public const string AccountName = "devstoreaccount1";
+
+ public const string AccountKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The full Docker image name, including the image repository and tag
+ /// (e.g., floci/floci-az:0.9.0).
+ ///
+ ///
+ /// Docker image tags available at .
+ ///
+ public FlociAzBuilder(string image)
+ : this(new DockerImage(image))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// An instance that specifies the Docker image to be used
+ /// for the container builder configuration.
+ ///
+ ///
+ /// Docker image tags available at .
+ ///
+ public FlociAzBuilder(IImage image)
+ : this(new FlociAzConfiguration())
+ {
+ DockerResourceConfiguration = Init().WithImage(image).DockerResourceConfiguration;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ private FlociAzBuilder(FlociAzConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ DockerResourceConfiguration = resourceConfiguration;
+ }
+
+ ///
+ protected override FlociAzConfiguration DockerResourceConfiguration { get; }
+
+ ///
+ public override FlociAzContainer Build()
+ {
+ Validate();
+ return new FlociAzContainer(DockerResourceConfiguration);
+ }
+
+ ///
+ protected override FlociAzBuilder Init()
+ {
+ return base.Init()
+ .WithPortBinding(FlociAzPort, true)
+ .WithPortBinding(EventHubsPort, true)
+ .WithPortBinding(ServiceBusPort, true)
+ .WithConnectionStringProvider(new FlociAzConnectionStringProvider())
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(request =>
+ request.ForPath("/_floci/health").ForPort(FlociAzPort)));
+ }
+
+ ///
+ protected override FlociAzBuilder Clone(IResourceConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new FlociAzConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override FlociAzBuilder Clone(IContainerConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new FlociAzConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override FlociAzBuilder Merge(FlociAzConfiguration oldValue, FlociAzConfiguration newValue)
+ {
+ return new FlociAzBuilder(new FlociAzConfiguration(oldValue, newValue));
+ }
+}
diff --git a/src/Testcontainers.FlociAz/FlociAzConfiguration.cs b/src/Testcontainers.FlociAz/FlociAzConfiguration.cs
new file mode 100644
index 000000000..b24d5f5dc
--- /dev/null
+++ b/src/Testcontainers.FlociAz/FlociAzConfiguration.cs
@@ -0,0 +1,41 @@
+namespace Testcontainers.FlociAz;
+
+///
+[PublicAPI]
+public sealed class FlociAzConfiguration : ContainerConfiguration
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public FlociAzConfiguration()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public FlociAzConfiguration(IResourceConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The container configuration.
+ public FlociAzConfiguration(IContainerConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The old FlociAz configuration.
+ /// The new FlociAz configuration.
+ public FlociAzConfiguration(FlociAzConfiguration oldValue, FlociAzConfiguration newValue)
+ : base(oldValue, newValue)
+ {
+ }
+}
diff --git a/src/Testcontainers.FlociAz/FlociAzConnectionStringProvider.cs b/src/Testcontainers.FlociAz/FlociAzConnectionStringProvider.cs
new file mode 100644
index 000000000..95c2881fc
--- /dev/null
+++ b/src/Testcontainers.FlociAz/FlociAzConnectionStringProvider.cs
@@ -0,0 +1,13 @@
+namespace Testcontainers.FlociAz;
+
+///
+/// Provides the FlociAz connection string.
+///
+internal sealed class FlociAzConnectionStringProvider : ContainerConnectionStringProvider
+{
+ ///
+ protected override string GetHostConnectionString()
+ {
+ return Container.GetConnectionString();
+ }
+}
diff --git a/src/Testcontainers.FlociAz/FlociAzContainer.cs b/src/Testcontainers.FlociAz/FlociAzContainer.cs
new file mode 100644
index 000000000..947d71424
--- /dev/null
+++ b/src/Testcontainers.FlociAz/FlociAzContainer.cs
@@ -0,0 +1,78 @@
+namespace Testcontainers.FlociAz;
+
+///
+[PublicAPI]
+public sealed class FlociAzContainer : DockerContainer
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The container configuration.
+ public FlociAzContainer(FlociAzConfiguration configuration)
+ : base(configuration)
+ {
+ }
+
+ ///
+ /// Gets the FlociAz storage connection string.
+ ///
+ /// The FlociAz storage connection string.
+ public string GetConnectionString()
+ {
+ var properties = new Dictionary();
+ properties.Add("DefaultEndpointsProtocol", Uri.UriSchemeHttp);
+ properties.Add("AccountName", FlociAzBuilder.AccountName);
+ properties.Add("AccountKey", FlociAzBuilder.AccountKey);
+ properties.Add("BlobEndpoint", GetServiceEndpoint());
+ properties.Add("QueueEndpoint", GetServiceEndpoint("queue"));
+ properties.Add("TableEndpoint", GetServiceEndpoint("table"));
+ return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
+ }
+
+ ///
+ /// Gets the FlociAz service endpoint.
+ ///
+ ///
+ /// FlociAz routes its REST services by path: the blob storage service uses the
+ /// bare /{accountName} path (omit the argument),
+ /// other services use /{accountName}-{service} (e.g., queue,
+ /// table, cosmos, keyvault, appconfig, functions).
+ ///
+ /// The service name, or null for the blob storage endpoint.
+ /// The FlociAz service endpoint.
+ public string GetServiceEndpoint(string service = null)
+ {
+ var path = string.IsNullOrEmpty(service) ? FlociAzBuilder.AccountName : string.Join("-", FlociAzBuilder.AccountName, service);
+ return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(FlociAzBuilder.FlociAzPort), path).ToString();
+ }
+
+ ///
+ /// Gets the FlociAz Cosmos DB connection string.
+ ///
+ /// The FlociAz Cosmos DB connection string.
+ public string GetCosmosConnectionString()
+ {
+ var properties = new Dictionary();
+ properties.Add("AccountEndpoint", GetServiceEndpoint("cosmos"));
+ properties.Add("AccountKey", FlociAzBuilder.AccountKey);
+ return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
+ }
+
+ ///
+ /// Gets the FlociAz Event Hubs endpoint.
+ ///
+ /// The FlociAz Event Hubs endpoint.
+ public string GetEventHubsEndpoint()
+ {
+ return new UriBuilder("amqp", Hostname, GetMappedPublicPort(FlociAzBuilder.EventHubsPort)).ToString();
+ }
+
+ ///
+ /// Gets the FlociAz Service Bus endpoint.
+ ///
+ /// The FlociAz Service Bus endpoint.
+ public string GetServiceBusEndpoint()
+ {
+ return new UriBuilder("amqp", Hostname, GetMappedPublicPort(FlociAzBuilder.ServiceBusPort)).ToString();
+ }
+}
diff --git a/src/Testcontainers.FlociAz/Testcontainers.FlociAz.csproj b/src/Testcontainers.FlociAz/Testcontainers.FlociAz.csproj
new file mode 100644
index 000000000..16967e2c6
--- /dev/null
+++ b/src/Testcontainers.FlociAz/Testcontainers.FlociAz.csproj
@@ -0,0 +1,12 @@
+
+
+ net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1
+ latest
+
+
+
+
+
+
+
+
diff --git a/src/Testcontainers.FlociAz/Usings.cs b/src/Testcontainers.FlociAz/Usings.cs
new file mode 100644
index 000000000..e6a7c21fd
--- /dev/null
+++ b/src/Testcontainers.FlociAz/Usings.cs
@@ -0,0 +1,9 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Linq;
+global using Docker.DotNet.Models;
+global using DotNet.Testcontainers.Builders;
+global using DotNet.Testcontainers.Configurations;
+global using DotNet.Testcontainers.Containers;
+global using DotNet.Testcontainers.Images;
+global using JetBrains.Annotations;
diff --git a/tests/Testcontainers.FlociAz.Tests/.editorconfig b/tests/Testcontainers.FlociAz.Tests/.editorconfig
new file mode 100644
index 000000000..78b36ca08
--- /dev/null
+++ b/tests/Testcontainers.FlociAz.Tests/.editorconfig
@@ -0,0 +1 @@
+root = true
diff --git a/tests/Testcontainers.FlociAz.Tests/.runs-on b/tests/Testcontainers.FlociAz.Tests/.runs-on
new file mode 100644
index 000000000..2c0c1ac72
--- /dev/null
+++ b/tests/Testcontainers.FlociAz.Tests/.runs-on
@@ -0,0 +1 @@
+ubuntu-24.04
diff --git a/tests/Testcontainers.FlociAz.Tests/Dockerfile b/tests/Testcontainers.FlociAz.Tests/Dockerfile
new file mode 100644
index 000000000..10c218371
--- /dev/null
+++ b/tests/Testcontainers.FlociAz.Tests/Dockerfile
@@ -0,0 +1 @@
+FROM floci/floci-az:0.9.0@sha256:5e403a7d788c24ab2d1dc03d4803ef2d3962ca01522abba65ed86f221ae87ac0
diff --git a/tests/Testcontainers.FlociAz.Tests/FlociAzContainerTest.cs b/tests/Testcontainers.FlociAz.Tests/FlociAzContainerTest.cs
new file mode 100644
index 000000000..752a028a3
--- /dev/null
+++ b/tests/Testcontainers.FlociAz.Tests/FlociAzContainerTest.cs
@@ -0,0 +1,102 @@
+namespace Testcontainers.FlociAz;
+
+public sealed class FlociAzContainerTest : IAsyncLifetime
+{
+ private const string AzureService = "Service";
+
+ private readonly FlociAzContainer _flociAzContainer = new FlociAzBuilder(TestSession.GetImageFromDockerfile()).Build();
+
+ public async ValueTask InitializeAsync()
+ {
+ await _flociAzContainer.StartAsync()
+ .ConfigureAwait(false);
+ }
+
+ public ValueTask DisposeAsync()
+ {
+ return _flociAzContainer.DisposeAsync();
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ [Trait(AzureService, "blob")]
+ public async Task DownloadBlobReturnsUploadedBlob()
+ {
+ // Given
+ var content = Guid.NewGuid().ToString("D");
+
+ var client = new BlobServiceClient(_flociAzContainer.GetConnectionString());
+
+ var containerClient = client.GetBlobContainerClient(Guid.NewGuid().ToString("D"));
+
+ var blobClient = containerClient.GetBlobClient(Guid.NewGuid().ToString("D"));
+
+ // When
+ _ = await containerClient.CreateAsync(cancellationToken: TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ _ = await blobClient.UploadAsync(BinaryData.FromString(content), cancellationToken: TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ var downloadResult = await blobClient.DownloadContentAsync(TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.Equal(content, downloadResult.Value.Content.ToString());
+ Assert.Equal(_flociAzContainer.GetConnectionString(), _flociAzContainer.GetConnectionString(ConnectionMode.Host));
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ [Trait(AzureService, "queue")]
+ public async Task ReceiveMessageReturnsSentMessage()
+ {
+ // Given
+ var message = Guid.NewGuid().ToString("D");
+
+ var client = new QueueServiceClient(_flociAzContainer.GetConnectionString());
+
+ var queueClient = client.GetQueueClient(Guid.NewGuid().ToString("D"));
+
+ // When
+ _ = await queueClient.CreateAsync(cancellationToken: TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ _ = await queueClient.SendMessageAsync(message, TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ var receivedMessage = await queueClient.ReceiveMessageAsync(cancellationToken: TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.Equal(message, receivedMessage.Value.MessageText);
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ [Trait(AzureService, "table")]
+ public async Task GetEntityReturnsAddedEntity()
+ {
+ // Given
+ var partitionKey = Guid.NewGuid().ToString("D");
+
+ var rowKey = Guid.NewGuid().ToString("D");
+
+ var client = new TableServiceClient(_flociAzContainer.GetConnectionString());
+
+ var tableClient = client.GetTableClient("Table" + Guid.NewGuid().ToString("N"));
+
+ // When
+ _ = await tableClient.CreateAsync(TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ _ = await tableClient.AddEntityAsync(new TableEntity(partitionKey, rowKey), TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ var entityResponse = await tableClient.GetEntityAsync(partitionKey, rowKey, cancellationToken: TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.Equal(rowKey, entityResponse.Value.RowKey);
+ }
+}
diff --git a/tests/Testcontainers.FlociAz.Tests/Testcontainers.FlociAz.Tests.csproj b/tests/Testcontainers.FlociAz.Tests/Testcontainers.FlociAz.Tests.csproj
new file mode 100644
index 000000000..da101b543
--- /dev/null
+++ b/tests/Testcontainers.FlociAz.Tests/Testcontainers.FlociAz.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+ net10.0
+ false
+ false
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
diff --git a/tests/Testcontainers.FlociAz.Tests/Usings.cs b/tests/Testcontainers.FlociAz.Tests/Usings.cs
new file mode 100644
index 000000000..5f185dc68
--- /dev/null
+++ b/tests/Testcontainers.FlociAz.Tests/Usings.cs
@@ -0,0 +1,8 @@
+global using System;
+global using System.Threading.Tasks;
+global using Azure.Data.Tables;
+global using Azure.Storage.Blobs;
+global using Azure.Storage.Queues;
+global using DotNet.Testcontainers.Commons;
+global using DotNet.Testcontainers.Configurations;
+global using Xunit;