Repository: kubermatic/machine-controller
Pull request #1175 (2022-01-25 12:54:20)

Make anexia provider more resilient against errors
Signed-off-by: kstiehl <kevin.stiehl@numericas.de>

**What this PR does / why we need it**:

The Anexia API can behave a little bit weird sometimes. 
* Requests to the API get a negative status code because of some internal engine timeouts but in the VM get's provisionend anyways. We are now tracking whether we already provisionend an IP, and stop provisioning more of them on error.
* During deletion we use the new deprovisioning tracking
* The machine controller remembers ongoing provisionings and reattaches to them now after an error

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>` format, will close the issue(s) when PR gets merged)*:
Fixes #

**Special notes for your reviewer**:

**Optional Release Note**:
<!--
Write your release note:
1. Enter your extended release note in the below block. If the PR requires additional action from users switching to the new release, include the string "action required".
2. If no release note is required, just write "NONE".
-->
```release-note
Anexia Provider is more resilient now and supports reattaching to ongoing provisionings after an error
```


kubermatic-bot: @kstiehl: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
kubermatic-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubermatic/machine-controller/pull/1175#" title="Author self-approved">kstiehl</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **ahmedwaleedmalik** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @ahmedwaleedmalik` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubermatic%2Fmachine-controller).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubermatic/machine-controller/blob/master/OWNERS)**
- **[pkg/cloudprovider/provider/OWNERS](https://github.com/kubermatic/machine-controller/blob/master/pkg/cloudprovider/provider/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["ahmedwaleedmalik"]} -->
kubermatic-bot: Hi @kstiehl. Thanks for your PR.

I'm waiting for a [kubermatic](https://github.com/orgs/kubermatic/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/kubermatic/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubermatic%2Fmachine-controller).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

moadqassem: @kstiehl Thank you very much for your effort. Can you please rebase your changes so we can start the reviewing process? Cheers! 
LittleFox94: @moadqassem just randomly saw your request and will do that on @kstiehl's behalf, as he does not work for us anymore - commits will say unverified here on github though
ahmedwaleedmalik: /retest
kubermatic-bot: @kstiehl: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-spot-instance | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-spot-instance/1504693614549143552) | true | `/test pull-machine-controller-e2e-aws-spot-instance`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-spot-instance | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-spot-instance/1504693614549143552) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws-ebs-encryption-enabled | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-ebs-encryption-enabled/1504693612187750400) | true | `/test pull-machine-controller-e2e-aws-ebs-encryption-enabled`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-spot-instance | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-spot-instance/1504693614549143552) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws-ebs-encryption-enabled | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-ebs-encryption-enabled/1504693612187750400) | true | `/test pull-machine-controller-e2e-aws-ebs-encryption-enabled`
pull-machine-controller-e2e-ubuntu-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-ubuntu-upgrade/1504693610510028800) | true | `/test pull-machine-controller-e2e-ubuntu-upgrade`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-spot-instance | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-spot-instance/1504693614549143552) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws-ebs-encryption-enabled | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-ebs-encryption-enabled/1504693612187750400) | true | `/test pull-machine-controller-e2e-aws-ebs-encryption-enabled`
pull-machine-controller-e2e-ubuntu-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-ubuntu-upgrade/1504693610510028800) | true | `/test pull-machine-controller-e2e-ubuntu-upgrade`
pull-machine-controller-e2e-deployment-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-deployment-upgrade/1504693611353083904) | true | `/test pull-machine-controller-e2e-deployment-upgrade`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-spot-instance | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-spot-instance/1504693614549143552) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws-ebs-encryption-enabled | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-ebs-encryption-enabled/1504693612187750400) | true | `/test pull-machine-controller-e2e-aws-ebs-encryption-enabled`
pull-machine-controller-e2e-ubuntu-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-ubuntu-upgrade/1504693610510028800) | true | `/test pull-machine-controller-e2e-ubuntu-upgrade`
pull-machine-controller-e2e-deployment-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-deployment-upgrade/1504693611353083904) | true | `/test pull-machine-controller-e2e-deployment-upgrade`
pull-machine-controller-e2e-hetzner | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-hetzner/1504693608836501504) | true | `/test pull-machine-controller-e2e-hetzner`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-spot-instance | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-spot-instance/1504693614549143552) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws-ebs-encryption-enabled | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-ebs-encryption-enabled/1504693612187750400) | true | `/test pull-machine-controller-e2e-aws-ebs-encryption-enabled`
pull-machine-controller-e2e-ubuntu-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-ubuntu-upgrade/1504693610510028800) | true | `/test pull-machine-controller-e2e-ubuntu-upgrade`
pull-machine-controller-e2e-deployment-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-deployment-upgrade/1504693611353083904) | true | `/test pull-machine-controller-e2e-deployment-upgrade`
pull-machine-controller-e2e-hetzner | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-hetzner/1504693608836501504) | true | `/test pull-machine-controller-e2e-hetzner`
pull-machine-controller-e2e-gce | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-gce/1504693607997640704) | true | `/test pull-machine-controller-e2e-gce`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
ahmedwaleedmalik: @kstiehl can you please refactor the PR to take care of:
- pull-machine-controller-lint 
- pull-machine-controller-test 
- pull-machine-controller-verify-boilerplate

Other failures are not related to your PR and I'm taking a look at them.
kubermatic-bot: @kstiehl: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-machine-controller-verify-boilerplate | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-verify-boilerplate/1504693600624054272) | true | `/test pull-machine-controller-verify-boilerplate`
pull-machine-controller-lint | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-lint/1504693600556945408) | true | `/test pull-machine-controller-lint`
pull-machine-controller-test | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-test/1504693600686968832) | true | `/test pull-machine-controller-test`
pull-machine-controller-e2e-aws-flatcar-containerd | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-flatcar-containerd/1504693613710282752) | true | `/test pull-machine-controller-e2e-aws-flatcar-containerd`
pull-machine-controller-e2e-vsphere | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-vsphere/1504693609679556608) | true | `/test pull-machine-controller-e2e-vsphere`
pull-machine-controller-e2e-aws-spot-instance | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-spot-instance/1504693614549143552) | true | `/test pull-machine-controller-e2e-aws-spot-instance`
pull-machine-controller-e2e-aws-ebs-encryption-enabled | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-aws-ebs-encryption-enabled/1504693612187750400) | true | `/test pull-machine-controller-e2e-aws-ebs-encryption-enabled`
pull-machine-controller-e2e-ubuntu-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-ubuntu-upgrade/1504693610510028800) | true | `/test pull-machine-controller-e2e-ubuntu-upgrade`
pull-machine-controller-e2e-deployment-upgrade | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-deployment-upgrade/1504693611353083904) | true | `/test pull-machine-controller-e2e-deployment-upgrade`
pull-machine-controller-e2e-hetzner | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-hetzner/1504693608836501504) | true | `/test pull-machine-controller-e2e-hetzner`
pull-machine-controller-e2e-gce | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-gce/1504693607997640704) | true | `/test pull-machine-controller-e2e-gce`
pull-machine-controller-e2e-azure | 5a4d0de5ac9fd2c04aeb3b6097e5299fecd0daad | [link](https://public-prow.loodse.com/view/gs/prow-dev-public-data/pr-logs/pull/kubermatic_machine-controller/1175/pull-machine-controller-e2e-azure/1504693606353473536) | true | `/test pull-machine-controller-e2e-azure`

[Full PR test history](https://public-prow.loodse.com/pr-history?org=kubermatic&repo=machine-controller&pr=1175)

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
LittleFox94: @ahmedwaleedmalik I'll look into it
kubermatic-bot: Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

:memo: **Please follow instructions in the [contributing guide](https://github.com/kubermatic/machine-controller/blob/master/CONTRIBUTING.md) to update your commits with the DCO**

Full details of the Developer Certificate of Origin can be found at [developercertificate.org](https://developercertificate.org/).

**The list of commits missing DCO signoff**:

- [eb7ff85](https://github.com/kubermatic/machine-controller/commits/eb7ff8519a0bb1eb046a299a20f57eb610e7c8ec) Add missing boilerplate to files

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>


Patch:
diff --git a/go.mod b/go.mod
index abd9200b..7fb3d9fd 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/Masterminds/semver/v3 v3.1.1
 	github.com/Masterminds/sprig/v3 v3.2.2
 	github.com/aliyun/alibaba-cloud-sdk-go v1.61.751
-	github.com/anexia-it/go-anxcloud v0.3.8
+	github.com/anexia-it/go-anxcloud v0.3.26
 	github.com/aws/aws-sdk-go v1.36.2
 	github.com/coreos/container-linux-config-transpiler v0.9.0
 	github.com/davecgh/go-spew v1.1.1
diff --git a/go.sum b/go.sum
index 5cb0cbda..a078d09d 100644
--- a/go.sum
+++ b/go.sum
@@ -173,8 +173,9 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.61.751 h1:PX0jCn9kBBgaybsFltpmQ8F7O74h
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.751/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/andygrunwald/go-gerrit v0.0.0-20190120104749-174420ebee6c/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk=
-github.com/anexia-it/go-anxcloud v0.3.8 h1:+ZOVqUHwINTm9Q68GPVh+Q/c794Fe+2GahIVagNLjDg=
 github.com/anexia-it/go-anxcloud v0.3.8/go.mod h1:cevqezsbOJ4GBlAWaztfLKl9w4VzxJBt4ipgHORi3gw=
+github.com/anexia-it/go-anxcloud v0.3.26 h1:uStosj8srS6OA1OsPsMJBFqd4Znzl6fEhUv8b3+G8FU=
+github.com/anexia-it/go-anxcloud v0.3.26/go.mod h1:fiEBxEtBXx78/OWBJvL7+2o4TESrnEcrDYjLeonGkDw=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -320,6 +321,7 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
 github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -1004,6 +1006,7 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
 github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
 github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
 github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
@@ -1182,7 +1185,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
@@ -1606,7 +1608,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1987,7 +1988,6 @@ k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI=
 k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk=
 k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
 k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
-k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
 k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw=
 k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
 k8s.io/apiextensions-apiserver v0.0.0-20190918161926-8f644eb6e783/go.mod h1:xvae1SZB3E17UpV59AWc271W/Ph25N+bjPyR63X6tPY=
@@ -2026,7 +2026,6 @@ k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlm
 k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
 k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
 k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
-k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
 k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk=
 k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
 k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
@@ -2112,8 +2111,6 @@ k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
 k8s.io/kube-aggregator v0.19.0/go.mod h1:1Ln45PQggFAG8xOqWPIYMxUq8WNtpPnYsbUJ39DpF/A=
 k8s.io/kube-aggregator v0.19.4/go.mod h1:cTkvun110194d797AuThyydBBlgm+cKIFUeS2uzGJfU=
 k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
-k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
-k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
 k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
 k8s.io/kubectl v0.19.0/go.mod h1:gPCjjsmE6unJzgaUNXIFGZGafiUp5jh0If3F/x7/rRg=
 k8s.io/kubectl v0.19.4/go.mod h1:XPmlu4DJEYgD83pvZFeKF8+MSvGnYGqunbFSrJsqHv0=
@@ -2186,7 +2183,6 @@ sigs.k8s.io/structured-merge-diff v1.0.1/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8AP
 sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 sigs.k8s.io/structured-merge-diff/v4 v4.1.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
 sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
diff --git a/pkg/cloudprovider/provider/anexia/provider.go b/pkg/cloudprovider/provider/anexia/provider.go
index 646882e2..796a8333 100644
--- a/pkg/cloudprovider/provider/anexia/provider.go
+++ b/pkg/cloudprovider/provider/anexia/provider.go
@@ -23,10 +23,18 @@ import (
 	"errors"
 	"fmt"
 	"net/http"
+	"time"
+
+	"github.com/anexia-it/go-anxcloud/pkg/vsphere/provisioning/progress"
+	"github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/anexia/utils"
+	"k8s.io/apimachinery/pkg/api/meta"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/wait"
+	"k8s.io/klog"
 
 	anxclient "github.com/anexia-it/go-anxcloud/pkg/client"
 	anxaddr "github.com/anexia-it/go-anxcloud/pkg/ipam/address"
-	anxvsphere "github.com/anexia-it/go-anxcloud/pkg/vsphere"
+	"github.com/anexia-it/go-anxcloud/pkg/vsphere"
 	anxvm "github.com/anexia-it/go-anxcloud/pkg/vsphere/provisioning/vm"
 
 	"github.com/kubermatic/machine-controller/pkg/apis/cluster/common"
@@ -43,25 +51,239 @@ import (
 	k8stypes "k8s.io/apimachinery/pkg/types"
 )
 
-type Config struct {
-	Token      string
-	VlanID     string
-	LocationID string
-	TemplateID string
-	CPUs       int
-	Memory     int
-	DiskSize   int
-}
+const (
+	ProvisionedType = "Provisioned"
+)
 
 type provider struct {
 	configVarResolver *providerconfig.ConfigVarResolver
 }
 
-func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *providerconfigtypes.Config, error) {
+func (p *provider) Create(machine *clusterv1alpha1.Machine, data *cloudprovidertypes.ProviderData,
+	userdata string, networkConfig *cloudprovidertypes.NetworkConfig) (instance instance.Instance, retErr error) {
+	status := getProviderStatus(machine)
+	klog.V(3).Infof(fmt.Sprintf("'%s' has status %#v", machine.Name, status))
+
+	// ensure conditions are present on machine
+	ensureConditions(&status)
+
+	config, _, err := p.getConfig(machine.Spec.ProviderSpec)
+	if err != nil {
+		return nil, fmt.Errorf("unable to get provider config: %w", err)
+	}
+
+	ctx := utils.CreateReconcileContext(utils.ReconcileContext{
+		Status:       &status,
+		UserData:     userdata,
+		Config:       config,
+		ProviderData: data,
+		Machine:      machine,
+	})
+
+	client, err := getClient(config.Token)
+	if err != nil {
+		return nil, err
+	}
+
+	// make sure status is reflected in Machine Object
+	defer func() {
+		// if error occurs during updating the machine object don't override the original error
+		retErr = anxtypes.NewMultiError(retErr, updateMachineStatus(machine, status, data.Update))
+	}()
+
+	// check whether machine is already provisioning
+	if isAlreadyProvisioning(ctx) && status.ProvisioningID == "" {
+		klog.Info("ongoing provisioning detected")
+		err := waitForVM(ctx, client)
+		if err != nil {
+			return nil, err
+		}
+		return p.Get(machine, data)
+	}
+
+	// provision machine
+	err = provisionVM(ctx, client)
+	if err != nil {
+		return nil, err
+	}
+	return p.Get(machine, data)
+}
+
+func waitForVM(ctx context.Context, client anxclient.Client) error {
+	reconcileContext := utils.GetReconcileContext(ctx)
+	api := vsphere.NewAPI(client)
+	var identifier string
+	err := wait.PollImmediate(5*time.Second, 1*time.Minute, func() (bool, error) {
+		klog.V(2).Info("checking for VM with name ", reconcileContext.Machine.Name)
+		vms, err := api.Search().ByName(ctx, fmt.Sprintf("%%-%s", reconcileContext.Machine.Name))
+		if err != nil {
+			return false, nil
+		}
+		if len(vms) < 1 {
+			return false, nil
+		}
+		if len(vms) > 1 {
+			return false, errors.New("too many VMs returned by search")
+		}
+		identifier = vms[0].Identifier
+		return true, nil
+	})
+	if err != nil {
+		return err
+	}
+
+	reconcileContext.Status.InstanceID = identifier
+	return updateMachineStatus(reconcileContext.Machine, *reconcileContext.Status, reconcileContext.ProviderData.Update)
+}
+
+func provisionVM(ctx context.Context, client anxclient.Client) error {
+	reconcileContext := utils.GetReconcileContext(ctx)
+	vmAPI := vsphere.NewAPI(client)
+
+	ctx, cancel := context.WithTimeout(ctx, anxtypes.CreateRequestTimeout)
+	defer cancel()
+
+	status := reconcileContext.Status
+	if status.ProvisioningID == "" {
+		klog.V(2).Info(fmt.Sprintf("Machine '%s'  does not contain a provisioningID yet. Starting to provision",
+			reconcileContext.Machine.Name))
+
+		config := reconcileContext.Config
+		reservedIP, err := getIPAddress(ctx, client)
+		if err != nil {
+			return newError(common.CreateMachineError, "failed to reserve IP: %v", err)
+		}
+		networkInterfaces := []anxvm.Network{{
+			NICType: anxtypes.VmxNet3NIC,
+			IPs:     []string{reservedIP},
+			VLAN:    config.VlanID,
+		}}
+
+		vm := vmAPI.Provisioning().VM().NewDefinition(
+			config.LocationID,
+			"templates",
+			config.TemplateID,
+			reconcileContext.Machine.Name,
+			config.CPUs,
+			config.Memory,
+			config.DiskSize,
+			networkInterfaces,
+		)
+
+		vm.Script = base64.StdEncoding.EncodeToString([]byte(reconcileContext.UserData))
+
+		sshKey, err := ssh.NewKey()
+		if err != nil {
+			return newError(common.CreateMachineError, "failed to generate ssh key: %v", err)
+		}
+		vm.SSH = sshKey.PublicKey
+
+		provisionResponse, err := vmAPI.Provisioning().VM().Provision(ctx, vm, false)
+		meta.SetStatusCondition(&status.Conditions, v1.Condition{
+			Type:    ProvisionedType,
+			Status:  v1.ConditionFalse,
+			Reason:  "Provisioning",
+			Message: "provisioning request was sent",
+		})
+		if err != nil {
+			return newError(common.CreateMachineError, "instance provisioning failed: %v", err)
+		}
+
+		// we successfully sent a VM provisioning request to the API, we consider the IP as 'Bound' now
+		status.IPState = anxtypes.IPStateBound
+
+		status.ProvisioningID = provisionResponse.Identifier
+		err = updateMachineStatus(reconcileContext.Machine, *status, reconcileContext.ProviderData.Update)
+		if err != nil {
+			return err
+		}
+	}
+
+	klog.V(2).Info(fmt.Sprintf("Using provisionID from machine '%s' to await completion",
+		reconcileContext.Machine.Name))
+
+	instanceID, err := vmAPI.Provisioning().Progress().AwaitCompletion(ctx, status.ProvisioningID)
+	if err != nil {
+		klog.Errorf("failed to await machine completion '%s'", reconcileContext.Machine.Name)
+		// something went wrong remove provisioning ID, so we can start from scratch
+		status.ProvisioningID = ""
+		return newError(common.CreateMachineError, "instance provisioning failed: %v", err)
+	}
+
+	status.InstanceID = instanceID
+	meta.SetStatusCondition(&status.Conditions, v1.Condition{
+		Type:    ProvisionedType,
+		Status:  v1.ConditionTrue,
+		Reason:  "Provisioned",
+		Message: "Machine has been successfully created",
+	})
+
+	return updateMachineStatus(reconcileContext.Machine, *status, reconcileContext.ProviderData.Update)
+}
+
+func getIPAddress(ctx context.Context, client anxclient.Client) (string, error) {
+	reconcileContext := utils.GetReconcileContext(ctx)
+	status := reconcileContext.Status
+
+	// only use IP if it is still unbound
+	if status.ReservedIP != "" && status.IPState == anxtypes.IPStateUnbound {
+		klog.Info("reusing already provisioned ip", "IP", status.ReservedIP)
+		return status.ReservedIP, nil
+	}
+	klog.Info(fmt.Sprintf("Creating a new IP for machine ''%s", reconcileContext.Machine.Name))
+	addrAPI := anxaddr.NewAPI(client)
+	config := reconcileContext.Config
+	res, err := addrAPI.ReserveRandom(ctx, anxaddr.ReserveRandom{
+		LocationID: config.LocationID,
+		VlanID:     config.VlanID,
+		Count:      1,
+	})
+	if err != nil {
+		return "", newError(common.InvalidConfigurationMachineError, "failed to reserve an ip address: %v", err)
+	}
+	if len(res.Data) < 1 {
+		return "", newError(common.InsufficientResourcesMachineError, "no ip address is available for this machine")
+	}
+
+	ip := res.Data[0].Address
+	status.ReservedIP = ip
+	status.IPState = anxtypes.IPStateUnbound
+
+	return ip, nil
+}
+
+func isAlreadyProvisioning(ctx context.Context) bool {
+	status := utils.GetReconcileContext(ctx).Status
+	condition := meta.FindStatusCondition(status.Conditions, ProvisionedType)
+	lastChange := condition.LastTransitionTime.Time
+	const reasonInProvisioning = "InProvisioning"
+	if condition.Reason == reasonInProvisioning && time.Since(lastChange) > 5*time.Minute {
+		meta.SetStatusCondition(&status.Conditions, v1.Condition{
+			Type:    ProvisionedType,
+			Reason:  "ReInitialising",
+			Message: "Could not find ongoing VM provisioning",
+			Status:  v1.ConditionFalse,
+		})
+	}
+
+	return condition.Status == v1.ConditionFalse && condition.Reason == reasonInProvisioning
+}
+
+func ensureConditions(status *anxtypes.ProviderStatus) {
+	conditions := [...]v1.Condition{
+		{Type: ProvisionedType, Message: "", Status: v1.ConditionUnknown, Reason: "Initialising"},
+	}
+	for _, condition := range conditions {
+		if meta.FindStatusCondition(status.Conditions, condition.Type) == nil {
+			meta.SetStatusCondition(&status.Conditions, condition)
+		}
+	}
+}
+
+func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*anxtypes.Config, *providerconfigtypes.Config, error) {
 	if provSpec.Value == nil {
 		return nil, nil, fmt.Errorf("machine.spec.providerSpec.value is nil")
 	}
-
 	pconfig, err := providerconfigtypes.GetConfig(provSpec)
 	if err != nil {
 		return nil, nil, err
@@ -76,7 +298,7 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p
 		return nil, nil, err
 	}
 
-	c := Config{}
+	c := anxtypes.Config{}
 	c.Token, err = p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.Token, anxtypes.AnxTokenEnv)
 	if err != nil {
 		return nil, nil, fmt.Errorf("failed to get 'token': %v", err)
@@ -162,9 +384,9 @@ func (p *provider) Get(machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.P
 	if err != nil {
 		return nil, newError(common.InvalidConfigurationMachineError, "failed to create Anexia client: %v", err)
 	}
-	vsphere := anxvsphere.NewAPI(cli)
+	vsphereAPI := vsphere.NewAPI(cli)
 
-	status, err := getStatus(machine.Status.ProviderStatus)
+	status := getProviderStatus(machine)
 	if err != nil {
 		return nil, newError(common.InvalidConfigurationMachineError, "failed to get machine status: %v", err)
 	}
@@ -175,7 +397,7 @@ func (p *provider) Get(machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.P
 	ctx, cancel := context.WithTimeout(context.Background(), anxtypes.GetRequestTimeout)
 	defer cancel()
 
-	info, err := vsphere.Info().Get(ctx, status.InstanceID)
+	info, err := vsphereAPI.Info().Get(ctx, status.InstanceID)
 	if err != nil {
 		return nil, fmt.Errorf("failed get machine info: %w", err)
 	}
@@ -185,151 +407,99 @@ func (p *provider) Get(machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.P
 	}, nil
 }
 
-func (p *provider) GetCloudConfig(spec clusterv1alpha1.MachineSpec) (string, string, error) {
+func (p *provider) GetCloudConfig(_ clusterv1alpha1.MachineSpec) (string, string, error) {
 	return "", "", nil
 }
 
-// Create creates a cloud instance according to the given machine
-func (p *provider) Create(machine *clusterv1alpha1.Machine, data *cloudprovidertypes.ProviderData, userdata string, networkConfig *cloudprovidertypes.NetworkConfig) (instance.Instance, error) {
+func (p *provider) Cleanup(machine *clusterv1alpha1.Machine, data *cloudprovidertypes.ProviderData) (isDeleted bool, retErr error) {
+	status := getProviderStatus(machine)
+	// make sure status is reflected in Machine Object
+	defer func() {
+		// if error occurs during updating the machine object don't override the original error
+		retErr = anxtypes.NewMultiError(retErr, updateMachineStatus(machine, status, data.Update))
+	}()
+
+	ensureConditions(&status)
 	config, _, err := p.getConfig(machine.Spec.ProviderSpec)
 	if err != nil {
-		return nil, newError(common.InvalidConfigurationMachineError, "failed to parse MachineSpec: %v", err)
+		return false, newError(common.InvalidConfigurationMachineError, "failed to parse MachineSpec: %v", err)
 	}
 
 	cli, err := getClient(config.Token)
 	if err != nil {
-		return nil, newError(common.InvalidConfigurationMachineError, "failed to create Anexia client: %v", err)
+		return false, newError(common.InvalidConfigurationMachineError, "failed to create Anexia client: %v", err)
 	}
-	vsphere := anxvsphere.NewAPI(cli)
-	addr := anxaddr.NewAPI(cli)
-
-	ctx, cancel := context.WithTimeout(context.Background(), anxtypes.CreateRequestTimeout)
-	defer cancel()
+	vsphereAPI := vsphere.NewAPI(cli)
 
-	status, err := getStatus(machine.Status.ProviderStatus)
 	if err != nil {
-		return nil, newError(common.InvalidConfigurationMachineError, "failed to get machine status: %v", err)
+		return false, newError(common.InvalidConfigurationMachineError, "failed to get machine status: %v", err)
 	}
 
-	if status.ProvisioningID == "" {
-		res, err := addr.ReserveRandom(ctx, anxaddr.ReserveRandom{
-			LocationID: config.LocationID,
-			VlanID:     config.VlanID,
-			Count:      1,
-		})
-		if err != nil {
-			return nil, newError(common.InvalidConfigurationMachineError, "failed to reserve an ip address: %v", err)
-		}
-		if len(res.Data) < 1 {
-			return nil, newError(common.InsufficientResourcesMachineError, "no ip address is available for this machine")
-		}
-
-		networkInterfaces := []anxvm.Network{{
-			NICType: anxtypes.VmxNet3NIC,
-			IPs:     []string{res.Data[0].Address},
-			VLAN:    config.VlanID,
-		}}
-
-		vm := vsphere.Provisioning().VM().NewDefinition(
-			config.LocationID,
-			"templates",
-			config.TemplateID,
-			machine.ObjectMeta.Name,
-			config.CPUs,
-			config.Memory,
-			config.DiskSize,
-			networkInterfaces,
-		)
-
-		vm.Script = base64.StdEncoding.EncodeToString([]byte(userdata))
-
-		sshKey, err := ssh.NewKey()
-		if err != nil {
-			return nil, newError(common.CreateMachineError, "failed to generate ssh key: %v", err)
-		}
-		vm.SSH = sshKey.PublicKey
+	ctx, cancel := context.WithTimeout(context.Background(), anxtypes.DeleteRequestTimeout)
+	defer cancel()
 
-		provisionResponse, err := vsphere.Provisioning().VM().Provision(ctx, vm)
+	// first check whether there is an provisioning ongoing
+	if status.DeprovisioningID == "" {
+		response, err := vsphereAPI.Provisioning().VM().Deprovision(ctx, status.InstanceID, false)
 		if err != nil {
-			return nil, newError(common.CreateMachineError, "instance provisioning failed: %v", err)
-		}
-
-		status.ProvisioningID = provisionResponse.Identifier
-		if err := updateStatus(machine, status, data.Update); err != nil {
-			return nil, newError(common.UpdateMachineError, "machine status update failed: %v", err)
+			var respErr *anxclient.ResponseError
+			// Only error if the error was not "not found"
+			if !(errors.As(err, &respErr) && respErr.ErrorData.Code == http.StatusNotFound) {
+				return false, newError(common.DeleteMachineError, "failed to delete machine: %v", err)
+			}
 		}
+		status.DeprovisioningID = response.Identifier
 	}
 
-	instanceID, err := vsphere.Provisioning().Progress().AwaitCompletion(ctx, status.ProvisioningID)
-	if err != nil {
-		return nil, newError(common.CreateMachineError, "instance provisioning failed: %v", err)
-	}
-
-	status.InstanceID = instanceID
-	if err := updateStatus(machine, status, data.Update); err != nil {
-		return nil, newError(common.UpdateMachineError, "machine status update failed: %v", err)
-	}
-
-	return p.Get(machine, data)
+	return isTaskDone(ctx, cli, status.DeprovisioningID)
 }
 
-func (p *provider) Cleanup(machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.ProviderData) (bool, error) {
-	config, _, err := p.getConfig(machine.Spec.ProviderSpec)
-	if err != nil {
-		return false, newError(common.InvalidConfigurationMachineError, "failed to parse MachineSpec: %v", err)
-	}
-
-	cli, err := getClient(config.Token)
+func isTaskDone(ctx context.Context, cli anxclient.Client, progressIdentifier string) (bool, error) {
+	response, err := progress.NewAPI(cli).Get(ctx, progressIdentifier)
 	if err != nil {
-		return false, newError(common.InvalidConfigurationMachineError, "failed to create Anexia client: %v", err)
+		return false, err
 	}
-	vsphere := anxvsphere.NewAPI(cli)
 
-	status, err := getStatus(machine.Status.ProviderStatus)
-	if err != nil {
-		return false, newError(common.InvalidConfigurationMachineError, "failed to get machine status: %v", err)
+	if len(response.Errors) != 0 {
+		taskErrors, _ := json.Marshal(response.Errors)
+		return true, fmt.Errorf("task failed with: %s", taskErrors)
 	}
 
-	ctx, cancel := context.WithTimeout(context.Background(), anxtypes.DeleteRequestTimeout)
-	defer cancel()
-
-	err = vsphere.Provisioning().VM().Deprovision(ctx, status.InstanceID, false)
-	if err != nil {
-		var respErr *anxclient.ResponseError
-		// Only error if the error was not "not found"
-		if !(errors.As(err, &respErr) && respErr.ErrorData.Code == http.StatusNotFound) {
-			return false, newError(common.DeleteMachineError, "failed to delete machine: %v", err)
-		}
+	if response.Progress == 100 {
+		return true, nil
 	}
 
-	return true, nil
+	return false, nil
 }
 
 func (p *provider) MigrateUID(_ *clusterv1alpha1.Machine, _ k8stypes.UID) error {
 	return nil
 }
 
-func (p *provider) MachineMetricsLabels(machine *clusterv1alpha1.Machine) (map[string]string, error) {
+func (p *provider) MachineMetricsLabels(_ *clusterv1alpha1.Machine) (map[string]string, error) {
 	return map[string]string{}, nil
 }
 
-func (p *provider) SetMetricsForMachines(machine clusterv1alpha1.MachineList) error {
+func (p *provider) SetMetricsForMachines(_ clusterv1alpha1.MachineList) error {
 	return nil
 }
 
 func getClient(token string) (anxclient.Client, error) {
 	tokenOpt := anxclient.TokenFromString(token)
-	return anxclient.New(tokenOpt)
+	client := anxclient.HTTPClient(&http.Client{Timeout: 30 * time.Second})
+	return anxclient.New(tokenOpt, client)
 }
 
-func getStatus(rawStatus *runtime.RawExtension) (*anxtypes.ProviderStatus, error) {
-	var status anxtypes.ProviderStatus
-	if rawStatus != nil && rawStatus.Raw != nil {
-		if err := json.Unmarshal(rawStatus.Raw, &status); err != nil {
-			return nil, err
+func getProviderStatus(machine *clusterv1alpha1.Machine) anxtypes.ProviderStatus {
+	var providerStatus anxtypes.ProviderStatus
+	status := machine.Status.ProviderStatus
+	if status != nil && status.Raw != nil {
+		if err := json.Unmarshal(status.Raw, &providerStatus); err != nil {
+			klog.Warningf("Unable to parse status from machine object. status was discarded for machine")
+			return anxtypes.ProviderStatus{}
 		}
 	}
-	return &status, nil
+	return providerStatus
 }
 
 // newError creates a terminal error matching to the provider interface.
@@ -340,7 +510,9 @@ func newError(reason common.MachineStatusError, msg string, args ...interface{})
 	}
 }
 
-func updateStatus(machine *clusterv1alpha1.Machine, status *anxtypes.ProviderStatus, updater cloudprovidertypes.MachineUpdater) error {
+// updateMachineStatus tries to update the machine status by any means
+// an error will lead to a panic
+func updateMachineStatus(machine *clusterv1alpha1.Machine, status anxtypes.ProviderStatus, updater cloudprovidertypes.MachineUpdater) error {
 	rawStatus, err := json.Marshal(status)
 	if err != nil {
 		return err
@@ -350,6 +522,7 @@ func updateStatus(machine *clusterv1alpha1.Machine, status *anxtypes.ProviderSta
 			Raw: rawStatus,
 		}
 	})
+
 	if err != nil {
 		return err
 	}
diff --git a/pkg/cloudprovider/provider/anexia/types/errors.go b/pkg/cloudprovider/provider/anexia/types/errors.go
new file mode 100644
index 00000000..6e73d232
--- /dev/null
+++ b/pkg/cloudprovider/provider/anexia/types/errors.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2022 The Machine Controller Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package types
+
+import (
+	"fmt"
+	"strings"
+)
+
+// MultiError represent multiple errors at the same time.
+type MultiError []error
+
+func (r MultiError) Error() string {
+	errString := make([]string, len(r))
+	for i, err := range r {
+		errString[i] = fmt.Sprintf("Error %d: %s", i, err)
+	}
+	return fmt.Sprintf("Multiple errors occoured:\n%s", strings.Join(errString, "\n"))
+}
+
+func NewMultiError(errs ...error) error {
+	var combinedErr []error
+	for _, err := range errs {
+		if err == nil {
+			continue
+		}
+		combinedErr = append(combinedErr, err)
+	}
+
+	if len(combinedErr) > 0 {
+		return MultiError(combinedErr)
+	}
+
+	return nil
+}
diff --git a/pkg/cloudprovider/provider/anexia/types/types.go b/pkg/cloudprovider/provider/anexia/types/types.go
index 9bdc929f..1046315f 100644
--- a/pkg/cloudprovider/provider/anexia/types/types.go
+++ b/pkg/cloudprovider/provider/anexia/types/types.go
@@ -17,6 +17,9 @@ limitations under the License.
 package types
 
 import (
+	"github.com/kubermatic/machine-controller/pkg/apis/cluster/common"
+	cloudprovidererrors "github.com/kubermatic/machine-controller/pkg/cloudprovider/errors"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"time"
 
 	"github.com/kubermatic/machine-controller/pkg/jsonutil"
@@ -30,10 +33,18 @@ const (
 	GetRequestTimeout    = 1 * time.Minute
 	DeleteRequestTimeout = 1 * time.Minute
 
+	IPStateBound   = "Bound"
+	IPStateUnbound = "Unbound"
+
 	VmxNet3NIC       = "vmxnet3"
 	MachinePoweredOn = "poweredOn"
 )
 
+var StatusUpdateFailed = cloudprovidererrors.TerminalError{
+	Reason:  common.UpdateMachineError,
+	Message: "Unable to update the machine status",
+}
+
 type RawConfig struct {
 	Token      providerconfigtypes.ConfigVarString `json:"token,omitempty"`
 	VlanID     providerconfigtypes.ConfigVarString `json:"vlanID"`
@@ -45,9 +56,22 @@ type RawConfig struct {
 }
 
 type ProviderStatus struct {
-	InstanceID     string `json:"instanceID"`
-	ProvisioningID string `json:"provisioningID"`
-	// TODO: add conditions to track progress on the provider side
+	InstanceID       string         `json:"instanceID"`
+	ProvisioningID   string         `json:"provisioningID"`
+	DeprovisioningID string         `json:"deprovisioningID"`
+	ReservedIP       string         `json:"reservedIP"`
+	IPState          string         `json:"ipState"`
+	Conditions       []v1.Condition `json:"conditions,omitempty"`
+}
+
+type Config struct {
+	Token      string
+	VlanID     string
+	LocationID string
+	TemplateID string
+	CPUs       int
+	Memory     int
+	DiskSize   int
 }
 
 func GetConfig(pconfig providerconfigtypes.Config) (*RawConfig, error) {
diff --git a/pkg/cloudprovider/provider/anexia/utils/utils.go b/pkg/cloudprovider/provider/anexia/utils/utils.go
new file mode 100644
index 00000000..97aa73d5
--- /dev/null
+++ b/pkg/cloudprovider/provider/anexia/utils/utils.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2022 The Machine Controller Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package utils
+
+import (
+	"context"
+
+	"github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
+	anxtypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/anexia/types"
+	cloudprovidertypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/types"
+)
+
+type contextKey byte
+
+const MachineReconcileContextKey contextKey = 0
+
+type ReconcileContext struct {
+	Machine      *v1alpha1.Machine
+	Status       *anxtypes.ProviderStatus
+	UserData     string
+	Config       *anxtypes.Config
+	ProviderData *cloudprovidertypes.ProviderData
+}
+
+func CreateReconcileContext(cc ReconcileContext) context.Context {
+	return context.WithValue(context.Background(), MachineReconcileContextKey, cc)
+}
+
+func GetReconcileContext(ctx context.Context) ReconcileContext {
+	rawContext := ctx.Value(MachineReconcileContextKey)
+	if recContext, ok := rawContext.(ReconcileContext); ok {
+		return recContext
+	}
+	return ReconcileContext{}
+}
diff --git a/pkg/controller/machine/machine_controller.go b/pkg/controller/machine/machine_controller.go
index 3ae4e140..2e8a5058 100644
--- a/pkg/controller/machine/machine_controller.go
+++ b/pkg/controller/machine/machine_controller.go
@@ -37,6 +37,7 @@ import (
 	cloudprovidertypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/types"
 	"github.com/kubermatic/machine-controller/pkg/cloudprovider/util"
 	"github.com/kubermatic/machine-controller/pkg/containerruntime"
+	controllerutil "github.com/kubermatic/machine-controller/pkg/controller/util"
 	kuberneteshelper "github.com/kubermatic/machine-controller/pkg/kubernetes"
 	"github.com/kubermatic/machine-controller/pkg/node/eviction"
 	"github.com/kubermatic/machine-controller/pkg/node/poddeletion"
@@ -223,7 +224,7 @@ func Add(
 		handler.EnqueueRequestsFromMapFunc(func(node client.Object) (result []reconcile.Request) {
 			machinesList := &clusterv1alpha1.MachineList{}
 			if err := mgr.GetClient().List(ctx, machinesList); err != nil {
-				utilruntime.HandleError(fmt.Errorf("Failed to list machines in lister: %v", err))
+				utilruntime.HandleError(fmt.Errorf("failed to list machines in lister: %v", err))
 				return
 			}
 
@@ -346,11 +347,11 @@ func (r *Reconciler) createProviderInstance(prov cloudprovidertypes.Provider, ma
 	if err != nil {
 		return nil, fmt.Errorf("failed to add %q finalizer: %v", FinalizerDeleteInstance, err)
 	}
-	instance, err := prov.Create(machine, r.providerData, userdata, networkConfig)
+	i, err := prov.Create(machine, r.providerData, userdata, networkConfig)
 	if err != nil {
 		return nil, err
 	}
-	return instance, nil
+	return i, nil
 }
 
 func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
@@ -810,7 +811,7 @@ func (r *Reconciler) ensureInstanceExistsForMachine(
 			var userdata string
 
 			if r.useOSM {
-				referencedMachineDeployment, err := r.getMachineDeploymentNameForMachine(ctx, machine)
+				referencedMachineDeployment, err := controllerutil.GetMachineDeploymentNameForMachine(ctx, machine, r.client)
 				if err != nil {
 					return nil, fmt.Errorf("failed to find machine's MachineDployment: %v", err)
 				}
@@ -1152,34 +1153,3 @@ func (r *Reconciler) updateNode(ctx context.Context, node *corev1.Node, modifier
 		return r.client.Update(ctx, node)
 	})
 }
-
-func (r *Reconciler) getMachineDeploymentNameForMachine(ctx context.Context, machine *clusterv1alpha1.Machine) (string, error) {
-	var (
-		machineSetName        string
-		machineDeploymentName string
-	)
-	for _, ownerRef := range machine.OwnerReferences {
-		if ownerRef.Kind == "MachineSet" {
-			machineSetName = ownerRef.Name
-		}
-	}
-
-	if machineSetName != "" {
-		machineSet := &clusterv1alpha1.MachineSet{}
-		if err := r.client.Get(ctx, types.NamespacedName{Name: machineSetName, Namespace: "kube-system"}, machineSet); err != nil {
-			return "", err
-		}
-
-		for _, ownerRef := range machineSet.OwnerReferences {
-			if ownerRef.Kind == "MachineDeployment" {
-				machineDeploymentName = ownerRef.Name
-			}
-		}
-
-		if machineDeploymentName != "" {
-			return machineDeploymentName, nil
-		}
-	}
-
-	return "", fmt.Errorf("failed to find machine deployment reference for the machine %s", machine.Name)
-}
diff --git a/pkg/controller/machinedeployment/rolling.go b/pkg/controller/machinedeployment/rolling.go
index 809ffa9e..4ca3496e 100644
--- a/pkg/controller/machinedeployment/rolling.go
+++ b/pkg/controller/machinedeployment/rolling.go
@@ -23,7 +23,7 @@ import (
 	"github.com/pkg/errors"
 
 	"github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
-	dutil "github.com/kubermatic/machine-controller/pkg/controller/machinedeployment/util"
+	dutil "github.com/kubermatic/machine-controller/pkg/controller/util"
 
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/klog"
diff --git a/pkg/controller/machinedeployment/sync.go b/pkg/controller/machinedeployment/sync.go
index e9c14203..51ede6fa 100644
--- a/pkg/controller/machinedeployment/sync.go
+++ b/pkg/controller/machinedeployment/sync.go
@@ -26,7 +26,7 @@ import (
 	"github.com/pkg/errors"
 
 	clusterv1alpha1 "github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
-	dutil "github.com/kubermatic/machine-controller/pkg/controller/machinedeployment/util"
+	dutil "github.com/kubermatic/machine-controller/pkg/controller/util"
 
 	"k8s.io/apimachinery/pkg/api/equality"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
diff --git a/pkg/controller/util/machine.go b/pkg/controller/util/machine.go
new file mode 100644
index 00000000..85a6c5c2
--- /dev/null
+++ b/pkg/controller/util/machine.go
@@ -0,0 +1,58 @@
+/*
+Copyright 2022 The Machine Controller Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package util
+
+import (
+	"context"
+	"fmt"
+
+	clusterv1alpha1 "github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
+
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func GetMachineDeploymentNameForMachine(ctx context.Context, machine *clusterv1alpha1.Machine, c client.Client) (string, error) {
+	var (
+		machineSetName        string
+		machineDeploymentName string
+	)
+	for _, ownerRef := range machine.OwnerReferences {
+		if ownerRef.Kind == "MachineSet" {
+			machineSetName = ownerRef.Name
+		}
+	}
+
+	if machineSetName != "" {
+		machineSet := &clusterv1alpha1.MachineSet{}
+		if err := c.Get(ctx, types.NamespacedName{Name: machineSetName, Namespace: "kube-system"}, machineSet); err != nil {
+			return "", err
+		}
+
+		for _, ownerRef := range machineSet.OwnerReferences {
+			if ownerRef.Kind == "MachineDeployment" {
+				machineDeploymentName = ownerRef.Name
+			}
+		}
+
+		if machineDeploymentName != "" {
+			return machineDeploymentName, nil
+		}
+	}
+
+	return "", fmt.Errorf("failed to find machine deployment reference for the machine %s", machine.Name)
+}
diff --git a/pkg/controller/machinedeployment/util/util.go b/pkg/controller/util/machine_deployment.go
similarity index 99%
rename from pkg/controller/machinedeployment/util/util.go
rename to pkg/controller/util/machine_deployment.go
index 42cb1152..48074279 100644
--- a/pkg/controller/machinedeployment/util/util.go
+++ b/pkg/controller/util/machine_deployment.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2019 The Machine Controller Authors.
+Copyright 2022 The Machine Controller Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -63,9 +63,6 @@ const (
 	// estimated once a deployment is paused.
 	PausedDeployReason = "DeploymentPaused"
 
-	//
-	// Available:
-	//
 	// MinimumReplicasAvailable is added in a deployment when it has its minimum replicas required available.
 	MinimumReplicasAvailable = "MinimumReplicasAvailable"
 	// MinimumReplicasUnavailable is added in a deployment when it doesn't have the minimum required replicas


Test patch:
diff --git a/pkg/cloudprovider/provider/anexia/helper_test.go b/pkg/cloudprovider/provider/anexia/helper_test.go
new file mode 100644
index 00000000..2edc3ba3
--- /dev/null
+++ b/pkg/cloudprovider/provider/anexia/helper_test.go
@@ -0,0 +1,91 @@
+/*
+Copyright 2022 The Machine Controller Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package anexia
+
+import (
+	"encoding/json"
+	"net/http"
+	"testing"
+
+	"github.com/anexia-it/go-anxcloud/pkg/vsphere/search"
+	"github.com/gophercloud/gophercloud/testhelper"
+	"github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
+	anxtypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/anexia/types"
+	"github.com/kubermatic/machine-controller/pkg/providerconfig/types"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+type ConfigTestCase struct {
+	Config anxtypes.RawConfig
+	Error  error
+}
+
+type ValidateCallTestCase struct {
+	Spec          v1alpha1.MachineSpec
+	ExpectedError error
+}
+
+func getSpecsForValidationTest(t *testing.T, configCases []ConfigTestCase) []ValidateCallTestCase {
+
+	var testCases []ValidateCallTestCase
+
+	for _, configCase := range configCases {
+		jsonConfig, err := json.Marshal(configCase.Config)
+		testhelper.AssertNoErr(t, err)
+		jsonProviderConfig, err := json.Marshal(types.Config{
+			CloudProviderSpec:   runtime.RawExtension{Raw: jsonConfig},
+			OperatingSystemSpec: runtime.RawExtension{Raw: []byte("{}")},
+		})
+		testhelper.AssertNoErr(t, err)
+		testCases = append(testCases, ValidateCallTestCase{
+			Spec: v1alpha1.MachineSpec{
+				ProviderSpec: v1alpha1.ProviderSpec{
+					Value: &runtime.RawExtension{Raw: jsonProviderConfig},
+				},
+			},
+			ExpectedError: configCase.Error,
+		})
+	}
+	return testCases
+}
+
+func createSearchHandler(t *testing.T, iterations int) http.HandlerFunc {
+	counter := 0
+	return func(writer http.ResponseWriter, request *http.Request) {
+		test := request.URL.Query().Get("name")
+		testhelper.AssertEquals(t, "%-TestMachine", test)
+		testhelper.TestMethod(t, request, http.MethodGet)
+		if iterations == counter {
+			encoder := json.NewEncoder(writer)
+			testhelper.AssertNoErr(t, encoder.Encode(map[string]interface{}{
+				"data": []search.VM{
+					{
+						Name:       "543053-TestMachine",
+						Identifier: TestIdentifier,
+					},
+				},
+			}))
+		}
+		counter++
+	}
+}
+
+func newConfigVarString(str string) types.ConfigVarString {
+	return types.ConfigVarString{
+		Value: str,
+	}
+}
diff --git a/pkg/cloudprovider/provider/anexia/provider_test.go b/pkg/cloudprovider/provider/anexia/provider_test.go
new file mode 100644
index 00000000..4fe74670
--- /dev/null
+++ b/pkg/cloudprovider/provider/anexia/provider_test.go
@@ -0,0 +1,333 @@
+/*
+Copyright 2022 The Machine Controller Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package anexia
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+	"testing"
+	"time"
+
+	anxclient "github.com/anexia-it/go-anxcloud/pkg/client"
+	"github.com/anexia-it/go-anxcloud/pkg/ipam/address"
+	"github.com/anexia-it/go-anxcloud/pkg/vsphere/provisioning/progress"
+	"github.com/anexia-it/go-anxcloud/pkg/vsphere/provisioning/vm"
+	"github.com/gophercloud/gophercloud/testhelper"
+	"github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
+	clusterv1alpha1 "github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1"
+	anxtypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/anexia/types"
+	"github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/anexia/utils"
+	cloudprovidertypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/types"
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+const TestIdentifier = "TestIdent"
+
+func TestAnexiaProvider(t *testing.T) {
+	testhelper.SetupHTTP()
+	client, server := anxclient.NewTestClient(nil, testhelper.Mux)
+	t.Cleanup(func() {
+		testhelper.TeardownHTTP()
+		server.Close()
+	})
+
+	t.Run("Test waiting for VM", func(t *testing.T) {
+		t.Parallel()
+
+		waitUntilVMIsFound := 2
+		testhelper.Mux.HandleFunc("/api/vsphere/v1/search/by_name.json", createSearchHandler(t, waitUntilVMIsFound))
+
+		providerStatus := anxtypes.ProviderStatus{}
+		ctx := utils.CreateReconcileContext(utils.ReconcileContext{
+			Machine: &v1alpha1.Machine{
+				ObjectMeta: metav1.ObjectMeta{Name: "TestMachine"},
+			},
+			Status:   &providerStatus,
+			UserData: "",
+			Config:   &anxtypes.Config{},
+
+			ProviderData: &cloudprovidertypes.ProviderData{
+				Update: func(m *clusterv1alpha1.Machine, mod ...cloudprovidertypes.MachineModifier) error {
+					return nil
+				},
+			},
+		})
+
+		err := waitForVM(ctx, client)
+		if err != nil {
+			t.Fatal("No error was expected", err)
+
+		}
+
+		if providerStatus.InstanceID != TestIdentifier {
+			t.Errorf("Excpected InstanceID to be set")
+		}
+	})
+
+	t.Run("Test provision VM", func(t *testing.T) {
+		t.Parallel()
+		testhelper.Mux.HandleFunc("/api/ipam/v1/address/reserve/ip/count.json", func(writer http.ResponseWriter, request *http.Request) {
+			err := json.NewEncoder(writer).Encode(address.ReserveRandomSummary{
+				Data: []address.ReservedIP{
+					{
+						ID:      "IP-ID",
+						Address: "8.8.8.8",
+					},
+				},
+			})
+			testhelper.AssertNoErr(t, err)
+		})
+
+		testhelper.Mux.HandleFunc("/api/vsphere/v1/provisioning/vm.json/LOCATION-ID/templates/TEMPLATE-ID", func(writer http.ResponseWriter, request *http.Request) {
+			testhelper.TestMethod(t, request, http.MethodPost)
+			type jsonObject = map[string]interface{}
+			expectedJSON := map[string]interface{}{
+				"cpu_performance_type": "performance",
+				"hostname":             "TestMachine",
+				"memory_mb":            json.Number("5"),
+				"network": []jsonObject{
+					{
+						"vlan":     "VLAN-ID",
+						"nic_type": "vmxnet3",
+						"ips":      []interface{}{"8.8.8.8"},
+					},
+				},
+			}
+			var jsonBody jsonObject
+			decoder := json.NewDecoder(request.Body)
+			decoder.UseNumber()
+			testhelper.AssertNoErr(t, decoder.Decode(&jsonBody))
+			testhelper.AssertEquals(t, expectedJSON["cpu_performance_type"], jsonBody["cpu_performance_type"])
+			testhelper.AssertEquals(t, expectedJSON["hostname"], jsonBody["hostname"])
+			testhelper.AssertEquals(t, expectedJSON["memory_mb"], jsonBody["memory_mb"])
+			testhelper.AssertEquals(t, expectedJSON["count"], jsonBody["count"])
+
+			expectedNetwork := expectedJSON["network"].([]jsonObject)[0]
+			bodyNetwork := jsonBody["network"].([]interface{})[0].(jsonObject)
+			testhelper.AssertEquals(t, expectedNetwork["vlan"], bodyNetwork["vlan"])
+			testhelper.AssertEquals(t, expectedNetwork["nic_type"], bodyNetwork["nic_type"])
+			testhelper.AssertEquals(t, expectedNetwork["ips"].([]interface{})[0], bodyNetwork["ips"].([]interface{})[0])
+
+			err := json.NewEncoder(writer).Encode(vm.ProvisioningResponse{
+				Progress:   100,
+				Errors:     nil,
+				Identifier: "TEST-IDENTIFIER",
+				Queued:     false,
+			})
+			testhelper.AssertNoErr(t, err)
+		})
+
+		testhelper.Mux.HandleFunc("/api/vsphere/v1/provisioning/progress.json/TEST-IDENTIFIER", func(writer http.ResponseWriter, request *http.Request) {
+			testhelper.TestMethod(t, request, http.MethodGet)
+
+			err := json.NewEncoder(writer).Encode(progress.Progress{
+				TaskIdentifier: "TEST-IDENTIFIER",
+				Queued:         false,
+				Progress:       100,
+				VMIdentifier:   "VM-IDENTIFIER",
+				Errors:         nil,
+			})
+			testhelper.AssertNoErr(t, err)
+		})
+
+		providerStatus := anxtypes.ProviderStatus{}
+		ctx := utils.CreateReconcileContext(utils.ReconcileContext{
+			Machine: &v1alpha1.Machine{
+				ObjectMeta: metav1.ObjectMeta{Name: "TestMachine"},
+			},
+			Status:   &providerStatus,
+			UserData: "",
+			Config: &anxtypes.Config{
+				VlanID:     "VLAN-ID",
+				LocationID: "LOCATION-ID",
+				TemplateID: "TEMPLATE-ID",
+				CPUs:       5,
+				Memory:     5,
+				DiskSize:   5,
+			},
+			ProviderData: &cloudprovidertypes.ProviderData{
+				Update: func(m *clusterv1alpha1.Machine, mods ...cloudprovidertypes.MachineModifier) error {
+					return nil
+				},
+			},
+		})
+
+		err := provisionVM(ctx, client)
+		testhelper.AssertNoErr(t, err)
+	})
+
+	t.Run("Test is VM Provisioning", func(t *testing.T) {
+		t.Parallel()
+		providerStatus := anxtypes.ProviderStatus{
+			Conditions: []metav1.Condition{
+				{
+					Type:   ProvisionedType,
+					Reason: "InProvisioning",
+					Status: metav1.ConditionFalse,
+				},
+			},
+		}
+		ctx := utils.CreateReconcileContext(utils.ReconcileContext{
+			Status:       &providerStatus,
+			UserData:     "",
+			Config:       nil,
+			ProviderData: nil,
+		})
+
+		condition := meta.FindStatusCondition(providerStatus.Conditions, ProvisionedType)
+		condition.LastTransitionTime = metav1.Time{Time: time.Now().Add(-1 * time.Minute)}
+		testhelper.AssertEquals(t, true, isAlreadyProvisioning(ctx))
+
+		condition.Reason = "Provisioned"
+		condition.Status = metav1.ConditionTrue
+		testhelper.AssertEquals(t, false, isAlreadyProvisioning(ctx))
+
+		condition.Reason = "InProvisioning"
+		condition.Status = metav1.ConditionFalse
+		condition.LastTransitionTime = metav1.Time{Time: time.Now().Add(-10 * time.Minute)}
+		testhelper.AssertEquals(t, false, isAlreadyProvisioning(ctx))
+		testhelper.AssertEquals(t, condition.Reason, "ReInitialising")
+	})
+
+	t.Run("Test getIPAddress", func(t *testing.T) {
+		t.Parallel()
+		providerStatus := &anxtypes.ProviderStatus{
+			ReservedIP: "",
+			IPState:    "",
+		}
+		ctx := utils.CreateReconcileContext(utils.ReconcileContext{Status: providerStatus})
+
+		t.Run("with unbound reserved IP", func(t *testing.T) {
+			expectedIP := "8.8.8.8"
+			providerStatus.ReservedIP = expectedIP
+			providerStatus.IPState = anxtypes.IPStateUnbound
+			reservedIP, err := getIPAddress(ctx, client)
+			testhelper.AssertNoErr(t, err)
+			testhelper.AssertEquals(t, expectedIP, reservedIP)
+		})
+	})
+}
+
+func TestValidate(t *testing.T) {
+	t.Parallel()
+
+	var configCases []ConfigTestCase
+	configCases = append(configCases,
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{},
+			Error:  errors.New("token is missing"),
+		},
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{Token: newConfigVarString("TEST-TOKEN")},
+			Error:  errors.New("cpu count is missing"),
+		},
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{Token: newConfigVarString("TEST-TOKEN"), CPUs: 1},
+			Error:  errors.New("disk size is missing"),
+		},
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{Token: newConfigVarString("TEST-TOKEN"), CPUs: 1, DiskSize: 5},
+			Error:  errors.New("memory size is missing"),
+		},
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{Token: newConfigVarString("TEST-TOKEN"), CPUs: 1, DiskSize: 5, Memory: 5},
+			Error:  errors.New("location id is missing"),
+		},
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{Token: newConfigVarString("TEST-TOKEN"), CPUs: 1, DiskSize: 5, Memory: 5,
+				LocationID: newConfigVarString("TLID")},
+			Error: errors.New("template id is missing"),
+		},
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{Token: newConfigVarString("TEST-TOKEN"), CPUs: 1, DiskSize: 5, Memory: 5,
+				LocationID: newConfigVarString("LID"), TemplateID: newConfigVarString("TID")},
+			Error: errors.New("vlan id is missing"),
+		},
+		ConfigTestCase{
+			Config: anxtypes.RawConfig{Token: newConfigVarString("TEST-TOKEN"), CPUs: 1, DiskSize: 5, Memory: 5,
+				LocationID: newConfigVarString("LID"), TemplateID: newConfigVarString("TID"), VlanID: newConfigVarString("VLAN")},
+			Error: nil,
+		},
+	)
+
+	provider := New(nil)
+	for _, testCase := range getSpecsForValidationTest(t, configCases) {
+		err := provider.Validate(testCase.Spec)
+		if testCase.ExpectedError != nil {
+			testhelper.AssertEquals(t, testCase.ExpectedError.Error(), err.Error())
+		} else {
+			testhelper.AssertEquals(t, testCase.ExpectedError, err)
+		}
+	}
+}
+
+func TestEnsureConditions(t *testing.T) {
+	t.Parallel()
+	status := anxtypes.ProviderStatus{}
+
+	ensureConditions(&status)
+
+	condition := meta.FindStatusCondition(status.Conditions, ProvisionedType)
+	if condition == nil {
+		t.Fatal("condition should not be nil")
+	}
+	testhelper.AssertEquals(t, metav1.ConditionUnknown, condition.Status)
+	testhelper.AssertEquals(t, "Initialising", condition.Reason)
+}
+
+func TestGetProviderStatus(t *testing.T) {
+	t.Parallel()
+
+	machine := &v1alpha1.Machine{}
+	providerStatus := anxtypes.ProviderStatus{
+		InstanceID: "InstanceID",
+	}
+	providerStatusJSON, err := json.Marshal(providerStatus)
+	testhelper.AssertNoErr(t, err)
+	machine.Status.ProviderStatus = &runtime.RawExtension{Raw: providerStatusJSON}
+
+	returnedStatus := getProviderStatus(machine)
+
+	testhelper.AssertEquals(t, "InstanceID", returnedStatus.InstanceID)
+
+}
+
+func TestUpdateStatus(t *testing.T) {
+	t.Parallel()
+	machine := &v1alpha1.Machine{}
+	providerStatus := anxtypes.ProviderStatus{
+		InstanceID: "InstanceID",
+	}
+	providerStatusJSON, err := json.Marshal(providerStatus)
+	testhelper.AssertNoErr(t, err)
+	machine.Status.ProviderStatus = &runtime.RawExtension{Raw: providerStatusJSON}
+
+	called := false
+	err = updateMachineStatus(machine, providerStatus, func(paramMachine *v1alpha1.Machine, modifier ...cloudprovidertypes.MachineModifier) error {
+		called = true
+		testhelper.AssertEquals(t, machine, paramMachine)
+		status := getProviderStatus(machine)
+		testhelper.AssertEquals(t, status.InstanceID, providerStatus.InstanceID)
+		return nil
+	})
+
+	testhelper.AssertEquals(t, true, called)
+	testhelper.AssertNoErr(t, err)
+}