diff --git a/prebuilt-tasks/pom.xml b/prebuilt-tasks/pom.xml
index 01501f526..bdb8e4652 100644
--- a/prebuilt-tasks/pom.xml
+++ b/prebuilt-tasks/pom.xml
@@ -16,6 +16,9 @@
2.6.1
20230227
6.5.0.202303070854-r
+ 1.37.0
+ 1.8.1
+ 2.24.0
@@ -134,6 +137,23 @@
spring-test
test
+
+
+ com.azure
+ azure-core
+ ${azure-core-version}
+
+
+ com.azure
+ azure-identity
+ ${azure-identity-version}
+
+
+ com.azure.resourcemanager
+ azure-resourcemanager
+ ${azure-resourcemanager-version}
+
+
diff --git a/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureCreateVirtualMachineTask.java b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureCreateVirtualMachineTask.java
new file mode 100644
index 000000000..8648c69ac
--- /dev/null
+++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureCreateVirtualMachineTask.java
@@ -0,0 +1,131 @@
+package com.redhat.parodos.tasks.azure;
+
+import java.util.List;
+
+import com.azure.resourcemanager.compute.models.AvailabilitySet;
+import com.azure.resourcemanager.compute.models.VirtualMachine;
+import com.azure.resourcemanager.network.models.Network;
+import com.azure.resourcemanager.network.models.NetworkInterface;
+import com.azure.resourcemanager.network.models.PublicIpAddress;
+import com.azure.resourcemanager.resources.models.ResourceGroup;
+import com.redhat.parodos.workflow.exception.MissingParameterException;
+import com.redhat.parodos.workflow.parameter.WorkParameter;
+import com.redhat.parodos.workflow.parameter.WorkParameterType;
+import com.redhat.parodos.workflow.task.enums.WorkFlowTaskOutput;
+import com.redhat.parodos.workflow.task.infrastructure.BaseInfrastructureWorkFlowTask;
+import com.redhat.parodos.workflows.work.DefaultWorkReport;
+import com.redhat.parodos.workflows.work.WorkContext;
+import com.redhat.parodos.workflows.work.WorkReport;
+import com.redhat.parodos.workflows.work.WorkStatus;
+import com.sun.jdi.InternalException;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class AzureCreateVirtualMachineTask extends BaseInfrastructureWorkFlowTask {
+
+ static final String VM_USER_NAME_KEY = "vm-user-name";
+ static final String VM_SSH_PUBLIC_KEY_KEY = "vm-ssh-public-key";
+ static final String AZURE_TENANT_ID_KEY = "azure-tenant-id";
+ static final String AZURE_SUBSCRIPTION_ID_KEY = "azure-subscription-id";
+ static final String AZURE_CLIENT_ID_KEY = "azure-client-id";
+ static final String AZURE_CLIENT_SECRET_KEY = "azure-client-secret";
+ static final String AZURE_RESOURCES_PREFIX_KEY = "azure-resources-prefix";
+
+ private AzureResourceClient azureResourceClient;
+
+ public AzureCreateVirtualMachineTask() {
+ this(new AzureResourceClientImpl());
+ }
+
+ AzureCreateVirtualMachineTask(AzureResourceClient azureResourceClient) {
+ this.azureResourceClient = azureResourceClient;
+ }
+
+ @Override
+ public @NonNull List getWorkFlowTaskParameters() {
+ return List.of(
+ WorkParameter.builder().key(VM_USER_NAME_KEY).description("The user name for the Virtual Machine login")
+ .type(WorkParameterType.TEXT).optional(false).build(),
+ WorkParameter.builder().key(VM_SSH_PUBLIC_KEY_KEY)
+ .description("The SSH public key for the Virtual Machine login").type(WorkParameterType.TEXT)
+ .optional(false).build(),
+ WorkParameter.builder().key(AZURE_TENANT_ID_KEY)
+ .description("The unique identifier of the Azure Active Directory instance")
+ .type(WorkParameterType.TEXT).optional(false).build(),
+ WorkParameter.builder().key(AZURE_SUBSCRIPTION_ID_KEY)
+ .description("The GUID that uniquely identifies your subscription to use Azure services")
+ .type(WorkParameterType.TEXT).optional(false).build(),
+ WorkParameter.builder().key(AZURE_CLIENT_ID_KEY).description(
+ "The unique Application (client) ID assigned to your app by Azure AD when the app was registered")
+ .type(WorkParameterType.TEXT).optional(false).build(),
+ WorkParameter.builder().key(AZURE_CLIENT_SECRET_KEY)
+ .description("The password of the service principal").type(WorkParameterType.TEXT)
+ .optional(false).build(),
+ WorkParameter.builder().key(AZURE_RESOURCES_PREFIX_KEY)
+ .description("A designated prefix for naming all Azure resources").type(WorkParameterType.TEXT)
+ .optional(false).build());
+ }
+
+ @Override
+ /**
+ * Execute task, creates Virtual Machine in Azure Results with Virtual Machine created
+ * with public IP. This VM can be accessed using ssh (with provided username and ssh
+ * key) the public IP address is added to the context returned when the task is
+ * completed. Key: public-ip-address
+ */
+ public WorkReport execute(WorkContext context) {
+ try {
+ final String userName = getRequiredParameterValue(context, VM_USER_NAME_KEY);
+ final String sshKey = getRequiredParameterValue(context, VM_SSH_PUBLIC_KEY_KEY);
+
+ final String azureTenantId = getRequiredParameterValue(context, AZURE_TENANT_ID_KEY);
+ final String azureSubscriptionId = getRequiredParameterValue(context, AZURE_SUBSCRIPTION_ID_KEY);
+ final String azureClientId = getRequiredParameterValue(context, AZURE_CLIENT_ID_KEY);
+ final String azureClientSecret = getRequiredParameterValue(context, AZURE_CLIENT_SECRET_KEY);
+ final String resourcesPrefix = getRequiredParameterValue(context, AZURE_RESOURCES_PREFIX_KEY);
+
+ this.azureResourceClient.init(azureTenantId, azureClientId, azureClientSecret, azureSubscriptionId);
+
+ ResourceGroup resourceGroup = azureResourceClient.createResourceGroup(resourcesPrefix);
+
+ AvailabilitySet availabilitySet = azureResourceClient.createAvailabilitySet(resourcesPrefix);
+
+ PublicIpAddress publicIPAddress = azureResourceClient.createPublicIpAddress(resourcesPrefix);
+
+ Network network = azureResourceClient.createNetwork(resourcesPrefix);
+
+ NetworkInterface networkInterface = azureResourceClient.createNetworkInterface(resourcesPrefix, network,
+ publicIPAddress);
+
+ VirtualMachine virtualMachine = azureResourceClient.createVirtualMachine(resourcesPrefix, networkInterface,
+ availabilitySet, userName, sshKey);
+
+ PublicIpAddress publicIpAddress = virtualMachine.getPrimaryPublicIPAddress();
+ if (publicIpAddress == null) {
+ log.error("VirtualMachine was created but without public IP");
+ throw new InternalException("The new created VirtualMachine missing public IP address");
+ }
+
+ String ipAddress = publicIpAddress.ipAddress();
+ log.info("VirtualMachine was created with public IP {}", ipAddress);
+ context.put("public-ip-address", ipAddress);
+ }
+ catch (MissingParameterException e) {
+ log.error("Task {} failed: missing required parameter, error: {}", getName(), e.getMessage());
+ return new DefaultWorkReport(WorkStatus.FAILED, context, e);
+ }
+ catch (Exception e) {
+ log.error("Task {} failed, with error: {}", getName(), e.getMessage());
+ return new DefaultWorkReport(WorkStatus.FAILED, context, e);
+ }
+
+ return new DefaultWorkReport(WorkStatus.COMPLETED, context);
+ }
+
+ @Override
+ public @NonNull List getWorkFlowTaskOutputs() {
+ return List.of(WorkFlowTaskOutput.OTHER);
+ }
+
+}
diff --git a/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureResourceClient.java b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureResourceClient.java
new file mode 100644
index 000000000..ee3aa046c
--- /dev/null
+++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureResourceClient.java
@@ -0,0 +1,27 @@
+package com.redhat.parodos.tasks.azure;
+
+import com.azure.resourcemanager.compute.models.AvailabilitySet;
+import com.azure.resourcemanager.compute.models.VirtualMachine;
+import com.azure.resourcemanager.network.models.Network;
+import com.azure.resourcemanager.network.models.NetworkInterface;
+import com.azure.resourcemanager.network.models.PublicIpAddress;
+import com.azure.resourcemanager.resources.models.ResourceGroup;
+
+interface AzureResourceClient {
+
+ void init(String azureTenantId, String azureClientId, String azureClientSecret, String azureSubscriptionId);
+
+ ResourceGroup createResourceGroup(String resourcesPrefix);
+
+ AvailabilitySet createAvailabilitySet(String resourcesPrefix);
+
+ PublicIpAddress createPublicIpAddress(String resourcesPrefix);
+
+ Network createNetwork(String resourcesPrefix);
+
+ NetworkInterface createNetworkInterface(String resourcesPrefix, Network network, PublicIpAddress publicIPAddress);
+
+ VirtualMachine createVirtualMachine(String resourcesPrefix, NetworkInterface networkInterface,
+ AvailabilitySet availabilitySet, String userName, String sshKey);
+
+}
diff --git a/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureResourceClientImpl.java b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureResourceClientImpl.java
new file mode 100644
index 000000000..862857f55
--- /dev/null
+++ b/prebuilt-tasks/src/main/java/com/redhat/parodos/tasks/azure/AzureResourceClientImpl.java
@@ -0,0 +1,85 @@
+package com.redhat.parodos.tasks.azure;
+
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.http.policy.HttpLogDetailLevel;
+import com.azure.core.management.AzureEnvironment;
+import com.azure.core.management.Region;
+import com.azure.core.management.profile.AzureProfile;
+import com.azure.identity.ClientSecretCredentialBuilder;
+import com.azure.resourcemanager.AzureResourceManager;
+import com.azure.resourcemanager.compute.models.AvailabilitySet;
+import com.azure.resourcemanager.compute.models.KnownLinuxVirtualMachineImage;
+import com.azure.resourcemanager.compute.models.VirtualMachine;
+import com.azure.resourcemanager.compute.models.VirtualMachineSizeTypes;
+import com.azure.resourcemanager.network.models.Network;
+import com.azure.resourcemanager.network.models.NetworkInterface;
+import com.azure.resourcemanager.network.models.PublicIpAddress;
+import com.azure.resourcemanager.resources.models.ResourceGroup;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+class AzureResourceClientImpl implements AzureResourceClient {
+
+ private AzureResourceManager azureResourceManager;
+
+ @Override
+ public void init(String azureTenantId, String azureClientId, String azureClientSecret, String azureSubscriptionId) {
+ TokenCredential credential = new ClientSecretCredentialBuilder().tenantId(azureTenantId).clientId(azureClientId)
+ .clientSecret(azureClientSecret).build();
+ AzureProfile profile = new AzureProfile(azureTenantId, azureSubscriptionId, AzureEnvironment.AZURE);
+ azureResourceManager = AzureResourceManager.configure().withLogLevel(HttpLogDetailLevel.BASIC)
+ .authenticate(credential, profile).withDefaultSubscription();
+ }
+
+ @Override
+ public ResourceGroup createResourceGroup(String resourcesPrefix) {
+ log.info("Creating resource group...");
+ return azureResourceManager.resourceGroups().define(resourcesPrefix + "ResourceGroup")
+ .withRegion(Region.US_EAST).create();
+ }
+
+ @Override
+ public AvailabilitySet createAvailabilitySet(String resourcesPrefix) {
+ log.info("Creating availability set...");
+ return azureResourceManager.availabilitySets().define(resourcesPrefix + "AvailabilitySet")
+ .withRegion(Region.US_EAST).withExistingResourceGroup(resourcesPrefix + "ResourceGroup").create();
+ }
+
+ @Override
+ public PublicIpAddress createPublicIpAddress(String resourcesPrefix) {
+ log.info("Creating public IP address...");
+ return azureResourceManager.publicIpAddresses().define(resourcesPrefix + "PublicIP").withRegion(Region.US_EAST)
+ .withExistingResourceGroup(resourcesPrefix + "ResourceGroup").withDynamicIP().create();
+ }
+
+ @Override
+ public Network createNetwork(String resourcesPrefix) {
+ log.info("Creating virtual network...");
+ return azureResourceManager.networks().define(resourcesPrefix + "VN").withRegion(Region.US_EAST)
+ .withExistingResourceGroup(resourcesPrefix + "ResourceGroup").withAddressSpace("10.0.0.0/16")
+ .withSubnet(resourcesPrefix + "Subnet", "10.0.0.0/24").create();
+ }
+
+ @Override
+ public NetworkInterface createNetworkInterface(String resourcesPrefix, Network network,
+ PublicIpAddress publicIPAddress) {
+ log.info("Creating network interface...");
+ return azureResourceManager.networkInterfaces().define(resourcesPrefix + "NIC").withRegion(Region.US_EAST)
+ .withExistingResourceGroup(resourcesPrefix + "ResourceGroup").withExistingPrimaryNetwork(network)
+ .withSubnet(resourcesPrefix + "Subnet").withPrimaryPrivateIPAddressDynamic()
+ .withExistingPrimaryPublicIPAddress(publicIPAddress).create();
+ }
+
+ @Override
+ public VirtualMachine createVirtualMachine(String resourcesPrefix, NetworkInterface networkInterface,
+ AvailabilitySet availabilitySet, String userName, String sshKey) {
+ log.info("Creating virtual machine...");
+ return azureResourceManager.virtualMachines().define(resourcesPrefix + "VM").withRegion(Region.US_EAST)
+ .withExistingResourceGroup(resourcesPrefix + "ResourceGroup")
+ .withExistingPrimaryNetworkInterface(networkInterface)
+ .withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_18_04_LTS).withRootUsername(userName)
+ .withSsh(sshKey).withComputerName(resourcesPrefix + "VM").withExistingAvailabilitySet(availabilitySet)
+ .withSize(VirtualMachineSizeTypes.STANDARD_D3_V2).create();
+ }
+
+}
diff --git a/prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/azure/AzureCreateVirtualMachineTaskTest.java b/prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/azure/AzureCreateVirtualMachineTaskTest.java
new file mode 100644
index 000000000..34b794d8f
--- /dev/null
+++ b/prebuilt-tasks/src/test/java/com/redhat/parodos/tasks/azure/AzureCreateVirtualMachineTaskTest.java
@@ -0,0 +1,154 @@
+package com.redhat.parodos.tasks.azure;
+
+import java.util.HashMap;
+
+import com.azure.resourcemanager.compute.models.AvailabilitySet;
+import com.azure.resourcemanager.compute.models.VirtualMachine;
+import com.azure.resourcemanager.network.models.Network;
+import com.azure.resourcemanager.network.models.NetworkInterface;
+import com.azure.resourcemanager.network.models.PublicIpAddress;
+import com.azure.resourcemanager.resources.models.ResourceGroup;
+import com.redhat.parodos.workflow.context.WorkContextDelegate;
+import com.redhat.parodos.workflow.exception.MissingParameterException;
+import com.redhat.parodos.workflows.work.WorkContext;
+import com.redhat.parodos.workflows.work.WorkReport;
+import com.redhat.parodos.workflows.work.WorkStatus;
+import com.sun.jdi.InternalException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class AzureCreateVirtualMachineTaskTest {
+
+ private static WorkContext ctx;
+
+ private static AzureResourceClient azureResourceClient;
+
+ // Define all Azure resources mocks
+ private static AzureCreateVirtualMachineTask underTest;
+
+ private static ResourceGroup resourceGroup;
+
+ private static AvailabilitySet availabilitySet;
+
+ private static PublicIpAddress publicIPAddress;
+
+ private static Network network;
+
+ private static NetworkInterface networkInterface;
+
+ private static VirtualMachine virtualMachine;
+
+ private static final String[] requiredParamsKeys = { AzureCreateVirtualMachineTask.VM_USER_NAME_KEY,
+ AzureCreateVirtualMachineTask.VM_SSH_PUBLIC_KEY_KEY, AzureCreateVirtualMachineTask.AZURE_TENANT_ID_KEY,
+ AzureCreateVirtualMachineTask.AZURE_SUBSCRIPTION_ID_KEY, AzureCreateVirtualMachineTask.AZURE_CLIENT_ID_KEY,
+ AzureCreateVirtualMachineTask.AZURE_CLIENT_SECRET_KEY,
+ AzureCreateVirtualMachineTask.AZURE_RESOURCES_PREFIX_KEY };
+
+ private static final String resourcesPrefix = AzureCreateVirtualMachineTask.AZURE_RESOURCES_PREFIX_KEY
+ + "-testValue";
+
+ private static final String userName = AzureCreateVirtualMachineTask.VM_USER_NAME_KEY + "-testValue";
+
+ private static final String sshKey = AzureCreateVirtualMachineTask.VM_SSH_PUBLIC_KEY_KEY + "-testValue";
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ // Initiate all Azure resources mocks
+ azureResourceClient = mock(AzureResourceClient.class);
+ resourceGroup = mock(ResourceGroup.class);
+ availabilitySet = mock(AvailabilitySet.class);
+ publicIPAddress = mock(PublicIpAddress.class);
+ network = mock(Network.class);
+ networkInterface = mock(NetworkInterface.class);
+ virtualMachine = mock(VirtualMachine.class);
+
+ underTest = new AzureCreateVirtualMachineTask(azureResourceClient);
+ underTest.setBeanName("AzureCreateVirtualMachineTask");
+ ctx = new WorkContext();
+
+ HashMap map = new HashMap<>();
+ for (String paramKey : requiredParamsKeys) {
+ map.put(paramKey, paramKey + "-testValue");
+ WorkContextDelegate.write(ctx, WorkContextDelegate.ProcessType.WORKFLOW_TASK_EXECUTION, underTest.getName(),
+ WorkContextDelegate.Resource.ARGUMENTS, map);
+ }
+
+ when(azureResourceClient.createResourceGroup(resourcesPrefix)).thenReturn(resourceGroup);
+ when(azureResourceClient.createAvailabilitySet(resourcesPrefix)).thenReturn(availabilitySet);
+ when(azureResourceClient.createPublicIpAddress(resourcesPrefix)).thenReturn(publicIPAddress);
+ when(azureResourceClient.createNetwork(resourcesPrefix)).thenReturn(network);
+ when(azureResourceClient.createNetworkInterface(resourcesPrefix, network, publicIPAddress))
+ .thenReturn(networkInterface);
+ when(azureResourceClient.createVirtualMachine(resourcesPrefix, networkInterface, availabilitySet, userName,
+ sshKey)).thenReturn(virtualMachine);
+ }
+
+ @Test
+ public void testHappyFlow() {
+ // given
+ String publicIP = "11.11.11.11";
+ when(virtualMachine.getPrimaryPublicIPAddress()).thenReturn(publicIPAddress);
+ when(publicIPAddress.ipAddress()).thenReturn(publicIP);
+
+ // when
+ WorkReport result = underTest.execute(ctx);
+
+ // then
+ verify(azureResourceClient, times(1)).createResourceGroup(resourcesPrefix);
+ verify(azureResourceClient, times(1)).createAvailabilitySet(resourcesPrefix);
+ verify(azureResourceClient, times(1)).createPublicIpAddress(resourcesPrefix);
+ verify(azureResourceClient, times(1)).createNetwork(resourcesPrefix);
+ verify(azureResourceClient, times(1)).createNetworkInterface(resourcesPrefix, network, publicIPAddress);
+ verify(azureResourceClient, times(1)).createVirtualMachine(resourcesPrefix, networkInterface, availabilitySet,
+ userName, sshKey);
+
+ assertEquals(WorkStatus.COMPLETED, result.getStatus());
+ assertEquals(publicIP, ctx.get("public-ip-address"));
+
+ }
+
+ @Test
+ public void testVmMissingPublicIPErr() {
+ // given
+ String publicIP = "11.11.11.11";
+ when(virtualMachine.getPrimaryPublicIPAddress()).thenReturn(null);
+
+ // when
+ WorkReport result = underTest.execute(ctx);
+
+ // then
+ assertEquals(WorkStatus.FAILED, result.getStatus());
+ assertEquals(InternalException.class, result.getError().getClass());
+ assertEquals("The new created VirtualMachine missing public IP address", result.getError().getMessage());
+
+ }
+
+ @Test
+ public void testMissingRequiredParamErr() {
+ // given
+ WorkContext ctx = new WorkContext();
+ HashMap map = new HashMap<>();
+
+ for (String paramKey : requiredParamsKeys) {
+ // when
+ WorkReport result = underTest.execute(ctx);
+
+ // then
+ assertEquals(WorkStatus.FAILED, result.getStatus());
+ assertEquals(MissingParameterException.class, result.getError().getClass());
+ assertEquals("missing parameter(s) for ParameterName: " + paramKey, result.getError().getMessage());
+
+ // Adding this key to the context for next for loop
+ map.put(paramKey, paramKey + "-testValue");
+ WorkContextDelegate.write(ctx, WorkContextDelegate.ProcessType.WORKFLOW_TASK_EXECUTION, underTest.getName(),
+ WorkContextDelegate.Resource.ARGUMENTS, map);
+ }
+ }
+
+}
diff --git a/workflow-examples/src/main/java/com/redhat/parodos/examples/azure/AzureVirtualMachineWorkFlowConfiguration.java b/workflow-examples/src/main/java/com/redhat/parodos/examples/azure/AzureVirtualMachineWorkFlowConfiguration.java
new file mode 100644
index 000000000..e91228566
--- /dev/null
+++ b/workflow-examples/src/main/java/com/redhat/parodos/examples/azure/AzureVirtualMachineWorkFlowConfiguration.java
@@ -0,0 +1,30 @@
+package com.redhat.parodos.examples.azure;
+
+import com.redhat.parodos.tasks.azure.AzureCreateVirtualMachineTask;
+import com.redhat.parodos.workflow.annotation.Infrastructure;
+import com.redhat.parodos.workflow.consts.WorkFlowConstants;
+import com.redhat.parodos.workflows.workflow.SequentialFlow;
+import com.redhat.parodos.workflows.workflow.WorkFlow;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AzureVirtualMachineWorkFlowConfiguration {
+
+ @Bean
+ AzureCreateVirtualMachineTask azureCreateVirtualMachineTask() {
+ return new AzureCreateVirtualMachineTask();
+ }
+
+ @Bean(name = "azureVirtualMachineWorkFlow" + WorkFlowConstants.INFRASTRUCTURE_WORKFLOW)
+ @Infrastructure
+ WorkFlow azureVirtualMachineWorkFlow(
+ @Qualifier("azureCreateVirtualMachineTask") AzureCreateVirtualMachineTask azureCreateVirtualMachineTask) {
+ return SequentialFlow.Builder.aNewSequentialFlow()
+ .named("azureVirtualMachineWorkFlow" + WorkFlowConstants.INFRASTRUCTURE_WORKFLOW)
+ .execute(azureCreateVirtualMachineTask).build();
+ }
+
+}
diff --git a/workflow-examples/src/main/java/com/redhat/parodos/examples/azure/README.md b/workflow-examples/src/main/java/com/redhat/parodos/examples/azure/README.md
new file mode 100644
index 000000000..6296ae052
--- /dev/null
+++ b/workflow-examples/src/main/java/com/redhat/parodos/examples/azure/README.md
@@ -0,0 +1,117 @@
+# Running AzureVirtualMachine WorkFlow Configuration instructions
+
+AzureVirtualMachineWorkFlow Configuration is an illustration of configuration utilizing the AzureVirtualMachineCreateVM Task.
+Below are guidelines on executing this example workflow configuration using curl.
+
+## 1. Prerequisites
+
+* Have account in azure, at least contributor user is required (In this case, second step should be requested from Admin).
+* You updated workflow-service running and accessible from your machine.
+
+## 2. Create App Registration in Azure account
+
+The workflow-service java application needs read and create permissions in your Azure subscription to run the AzureVirtualMachineCreateVM Task. Create a service principal, which its credentials would be used by the application. Service principals provide a way to create a noninteractive account associated with your identity to which you grant only the privileges your app needs to run.
+
+This step must be done by Admin user in Azure.
+It can be done only ones, make sure it has the required expiration date.
+
+### Configure the environment variable
+
+- **`AZURE_SUBSCRIPTION_ID`**: Use the id value from `az account show` in the Azure CLI 2.0.
+
+### Create a service principal by using the Azure CLI 2.0, and capture the output
+
+```shell
+az ad sp create-for-rbac \
+--name AzureJavaTest \
+--role Contributor \
+--scopes /subscriptions/${AZURE_SUBSCRIPTION_ID}
+```
+
+## 3. Execute the workflow using curl
+
+### Configure the environment variables
+
+- **`AZURE_SUBSCRIPTION_ID`**: Use the id value from `az account show` in the Azure CLI 2.0.
+- **`AZURE_CLIENT_ID`**: Use the appId value from the output taken from a service principal output.
+- **`AZURE_CLIENT_SECRET`**: Use the password value from the service principal output.
+- **`AZURE_TENANT_ID`**: Use the tenant value from the service principal output.
+- **`AZURE_RESOURCES_PREFIX`**: A designated prefix for naming all Azure resources
+- **`VM_USER_NAME`**: The username for the Virtual Machine login
+- **`VM_SSH_PUBLIC_KEY`**: The SSH public key for the Virtual Machine login
+- **`PARODOS_PROJECT_ID`**: Parodos project ID
+
+### Execute the workflow
+
+```shell
+curl -X 'POST' -u test:test \
+ 'http://localhost:8080/api/v1/workflows' \
+ -H 'accept: application/json' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "arguments": [],
+ "projectId": "'"${PARODOS_PROJECT_ID}"'",
+ "workFlowName": "azureVirtualMachineWorkFlow_INFRASTRUCTURE_WORKFLOW",
+ "works": [
+ {
+ "arguments": [
+ {
+ "key": "azure-tenant-id",
+ "value": "'"${AZURE_TENANT_ID}"'"
+ },
+ {
+ "key": "azure-subscription-id",
+ "value": "'"${AZURE_SUBSCRIPTION_ID}"'"
+ },
+ {
+ "key": "azure-client-id",
+ "value": "'"${AZURE_CLIENT_ID}"'"
+ },
+ {
+ "key": "azure-client-secret",
+ "value": "'"${AZURE_CLIENT_SECRET}"'"
+ },
+ {
+ "key": "azure-resources-prefix",
+ "value": "'"${AZURE_RESOURCES_PREFIX}"'"
+ },
+ {
+ "key": "vm-user-name",
+ "value": "'"${VM_USER_NAME}"'"
+ },
+ {
+ "key": "vm-ssh-public-key",
+ "value": "'"${VM_SSH_PUBLIC_KEY}"'"
+ }
+ ],
+ "type": "TASK",
+ "workName": "azureCreateVirtualMachineTask"
+ }
+ ]
+}'
+```
+## 4. Validate access to the VM
+
+### Configure the environment variables
+
+- **`VM_PUBLIC_IP`**: The public IP was assigned to the VM, you can find it in the workflow-service logs
+- **`VM_USER_NAME`**: The username for the Virtual Machine login
+- **`PRIVATE_SSH_KEY_PATH`**: The private ssh key which its public key was used for the VM creation in previous step
+
+### Access the VM using ssh
+
+```shell
+ssh -i ${PRIVATE_SSH_KEY_PATH} ${VM_USER_NAME}@${VM_PUBLIC_IP}
+```
+## 5. Delete all resources in Azure
+
+### Configure the environment variables
+
+- **`AZURE_RESOURCES_PREFIX`**: A designated prefix for naming all Azure resources
+
+### delete the VM
+
+```shell
+az group delete -n "${AZURE_RESOURCES_PREFIX}ResourceGroup"
+```
+