keboola / azure-api-client
Keboola Azure API client
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.5
- monolog/monolog: ^2.0|^3.0
- webmozart/assert: ^1.11
Requires (Dev)
- infection/infection: ^0.26.18
- keboola/coding-standard: ^15.0
- phpstan/phpstan: ^1.9
- phpstan/phpstan-phpunit: ^1.3
- phpstan/phpstan-webmozart-assert: ^1.2
- sempro/phpunit-pretty-print: ^1.4
- symfony/dotenv: ^6.2
- symfony/http-client: ^6.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 2.1.0
- 2.0.1
- 2.0.0
- 1.0.0
- dev-zajca-event-grid
- dev-pepa/gha
- dev-PST-2442-ondra
- dev-detached
- dev-devin/1767783154-bearer-token-api-bundle
- dev-devin/1769548300-improve-output-mapping-error-message
- dev-erik-AJDA-1087-single-job-v3
- dev-odin/AJDA-2841
- dev-pepa/PAT-1551_dropTimeout
- dev-pepa/symfony-httpclient-spike
- dev-pepa_PAT-64_podWatch
- dev-pepa_k8s_deployment
- dev-roman-pst-1710
- dev-pepa_azClientNamedArgs
This package is auto-updated.
Last update: 2026-10-26 12:13:49 UTC
README
Installation
composer require keboola/azure-api-client
Usage
The simplest way to use API client is just by creating its instance.
use Keboola\AzureApiClient\ApiClientConfiguration; use Keboola\AzureApiClient\Marketplace\MarketplaceApiClient; use Monolog\Logger; $logger = new Logger('azure-api-client'); $marketplaces = new MarketplaceApiClient(new ClientCredentialsAuth( logger: $logger, ));
Authentication
By default, API client will try to authenticate using ENV variables AZURE_TENANT_ID, AZURE_CLIENT_ID and
AZURE_CLIENT_SECRET. If some of them is not available, it'll fall back to Azure metadata API.
If you want to supply your own credentials, you can pass custom authenticator instance in the client options:
use Keboola\AzureApiClient\ApiClientConfiguration; use Keboola\AzureApiClient\Authentication\Authenticator\ClientCredentialsAuth; use Keboola\AzureApiClient\Marketplace\MarketplaceApiClient; $marketplaces = new MarketplaceApiClient(new ApiClientConfiguration( authenticator: new ClientCredentialsAuth( 'tenant-id', 'client-id', 'client-secret', ), ));
Or can provide custom authentication token directly:
use Keboola\AzureApiClient\ApiClientConfiguration; use Keboola\AzureApiClient\Authentication\Authenticator\StaticBearerTokenAuth; use Keboola\AzureApiClient\Marketplace\MarketplaceApiClient; $marketplaces = new MarketplaceApiClient(new ApiClientConfiguration( authenticator: new StaticBearerTokenAuth('my-token'), ));
Or even use custom authentication header if needed (this is how an Event Grid topic access key is sent):
use Keboola\AzureApiClient\ApiClientConfiguration; use Keboola\AzureApiClient\Authentication\Authenticator\CustomHeaderAuth; use Keboola\AzureApiClient\EventGrid\EventGridApiClient; $eventGrid = new EventGridApiClient('<topic>.<region>-1.eventgrid.azure.net', new ApiClientConfiguration( authenticator: new CustomHeaderAuth('aeg-sas-key', '<topic-access-key>'), ));
Services that accept Shared Access Signatures (Service Bus) can be authenticated with a shared access policy:
use Keboola\AzureApiClient\ApiClientConfiguration; use Keboola\AzureApiClient\Authentication\Authenticator\SharedAccessSignatureAuth; use Keboola\AzureApiClient\ServiceBus\ServiceBusApiClient; $serviceBus = new ServiceBusApiClient('https://<namespace>.servicebus.windows.net:443/', new ApiClientConfiguration( authenticator: new SharedAccessSignatureAuth('RootManageSharedAccessKey', '<shared-access-key>'), ));
Each request gets its own token, scoped to the request URI and valid for an hour (configurable by the third argument).
If even this is not enough for your use-case, you can implement your own
Keboola\AzureApiClient\Authentication\Authenticator\RequestAuthenticatorFactoryInterface and pass it as authenticator.
Timeouts and retries
Requests time out after 120 s (10 s to connect) and failed requests (5xx, 429, connection errors) are retried up to
10 times. All of it is set in ApiClientConfiguration:
new ApiClientConfiguration( backoffMaxTries: 0, // the caller retries on its own connectTimeout: 10, requestTimeout: 20, );
The defaults suit occasional calls, not a message consumer: with 10 retries and exponential backoff a single outage can block a call for many minutes, well past a Service Bus lock duration. Consumers should keep the whole retry budget below the lock duration and pass a real logger, retries are only visible as its warnings:
new ApiClientConfiguration( backoffMaxTries: 3, logger: $logger, connectTimeout: 10, requestTimeout: 20, );
Retries resend requests whose outcome is unknown:
ServiceBusApiClient::sendMessage()andEventGridApiClient::publishEvents()can deliver a message or event twice, so receivers must tolerate duplicates (a Service Bus queue can also deduplicate by message id).ServiceBusApiClient::deleteMessage()retried after a lost response finds the message already gone, and raisesMessageLockLostExceptionalthough the first attempt deleted it.
Redirects are not followed: the request would be authenticated again for the redirect target.
Service Bus
Send, peek-lock receive and delete (complete) of queue messages over the Service Bus REST API.
use Keboola\AzureApiClient\ServiceBus\Exception\MessageLockLostException; use Keboola\AzureApiClient\ServiceBus\Model\ServiceBusMessage; $serviceBus->sendMessage('my-queue', ServiceBusMessage::json('message-id', ['foo' => 'bar'])); $serviceBus->sendMessage('my-queue', ServiceBusMessage::json( 'delayed-message-id', ['foo' => 'bar'], scheduledEnqueueTime: new DateTimeImmutable('+5 minutes'), )); $message = $serviceBus->peekLockMessage('my-queue', waitSeconds: 10); // null when the queue stays empty if ($message !== null) { process($message->body, $message->brokerProperties->deliveryCount); try { $serviceBus->deleteMessage($message->lockLocation); } catch (MessageLockLostException) { // the lock expired before the delete, the message will be delivered again } }
waitSeconds has to be shorter than requestTimeout, otherwise the HTTP request would time out before Service Bus
answers.
Without an explicit authenticator the client uses an Entra token for https://servicebus.azure.net (see
Authentication); the identity needs the Azure Service Bus Data Sender role to send and Azure Service Bus Data Receiver to receive and
delete.
Event Grid
Publishes events in the Event Grid schema to a topic.
use Keboola\AzureApiClient\EventGrid\Model\EventGridEvent; $eventGrid->publishEvents([ new EventGridEvent( id: 'event-id', subject: 'subject', data: ['foo' => 'bar'], eventType: 'MyApp.SomethingHappened', ), ]);
Without an explicit authenticator the client uses an Entra token for https://eventgrid.azure.net; the identity
needs the EventGrid Data Sender role.
Development
Azure resources
Tests in the e2e PHPUnit group run against real Service Bus and Event Grid resources. They are part of
composer ci, so create the resources before running it. Log in to the Azure CLI (az login) with an identity that can
create resources, service principals and role assignments in the Keboola DEV Platform Services Team subscription.
Terraform keeps its state in the shared local-dev S3 bucket, so you also need AWS credentials that can assume the
kbc-local-dev-terraform role.
export NAME_PREFIX= # your name/nickname to make your resource unique & recognizable cat <<EOF > ./provisioning/local/terraform.tfvars name_prefix = "${NAME_PREFIX}" EOF terraform -chdir=./provisioning/local init -backend-config="key=azure-api-client/${NAME_PREFIX}.tfstate" terraform -chdir=./provisioning/local apply ./provisioning/local/update-env.sh docker compose run --rm dev-azure-api-client composer install docker compose run --rm dev-azure-api-client composer ci
update-env.sh writes the AZURE_API_CLIENT_TEST__* variables to .env.local. The Entra token tests run as a
service principal Terraform creates, with the same data roles a production sender and receiver get. Role assignments
can take a few minutes to propagate after the first apply; until then those tests fail with 401.
License
MIT licensed, see LICENSE file.