Skip to content

Conversation

@aeter
Copy link
Contributor

@aeter aeter commented Jan 8, 2026

Adds a terraform stackit_server option for the iaas ( create server ) API param: "agent": {"provisioned": true}

ref STACKITRCO-187

ref: stackitcloud/stackit-cli#1213


Tests:

  • ran make fmt, make generate-docs

  • ran unit tests

[~/terraform-provider-stackit] go test stackit/internal/services/iaas/server/*
ok      command-line-arguments  15.005s
[~/terraform-provider-stackit] make test
...
ok      github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/server       15.006s  coverage: 33.0% of statements
...
[~/terraform-provider-stackit]
  • Tested: with a locally-configured terraform - tested by adding, changing, deleting agent-related parts of the below main.tf ** Tested without providing agent inside main.tf (the result had "agent" = null /* object */)

** Tested with setting agent = { provisioning = true } (and then ran the stackit-cli command for checking if the agent was created successfully and able to run commands, stackit -y server command create --server-id=3fdac6ea-3885-441c-b473-bc94ca570ca8 --project-id=c904f41c-2f8c-4edb-b966-e87d65f10b64 --template-name=RunShellScript --params script='echo hello').

** Tested when setting agent = { provisioning = true } and then set it to false - verified the server was deleted and recreated again with the new value.

[~] cat main.tf
provider "stackit" {
  # Configuration options
  service_account_key_path = "/home/debian/terraform_dev/.terraform_key.json"
  default_region = "eu01"
}

resource "stackit_network_interface" "server_nic" {
  project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64"
  network_id = "97c5dde4-cb9d-49b8-be55-9cdf0c3795e1"
}

resource "stackit_server" "myserver1" {
    project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64"
    name = "terraformtestserver1"
    boot_volume = {
        size = 64
        source_type = "image"
        source_id = "21466190-b904-4267-8bf3-1be4323f4ffb"
        delete_on_termination = true
    }
    availability_zone = "eu01-1"
    agent = {
        provisioned = true
    }
    machine_type = "t1.1"
    network_interfaces = [ stackit_network_interface.server_nic.network_interface_id ]
}

data "stackit_server" "myserver1_data" {
   project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64"
   server_id  = stackit_server.myserver1.server_id
}

output "server_info_from_data" {
  value = data.stackit_server.myserver1_data
}
[~] terraform apply -auto-approve  ## this is without `agent` set in the config
...
server_info_from_data = {
  "affinity_group" = tostring(null)
  "agent" = null /* object */
  "availability_zone" = "eu01-1"
  ...
}
...
[~] terraform apply -auto-approve  ## this is with `agent = { provisioned = true }` set in the config
...
server_info_from_data = {
  "affinity_group" = tostring(null)
  "agent" = {
    "provisioned" = true
  }
  "availability_zone" = "eu01-1"
  "boot_volume" = {
    "delete_on_termination" = true
    "id" = "673021e5-2a90-4482-8ffa-e0485c7588bd"
  }
  ...
}

Description

relates to STACKITRCO-187

Checklist

  • Issue was linked above
  • Code format was applied: make fmt
  • Examples were added / adjusted (see examples/ directory)
  • Docs are up-to-date: make generate-docs (will be checked by CI)
  • Unit tests got implemented or updated
  • Acceptance tests got implemented or updated (see e.g. here)
  • Unit tests are passing: make test (will be checked by CI)
  • No linter issues: make lint (will be checked by CI)

@aeter aeter requested a review from a team as a code owner January 8, 2026 17:58
@aeter aeter force-pushed the feature/STACKITRCO-187-iaas-create-server-agent-param branch 2 times, most recently from 44c1522 to 699af50 Compare January 9, 2026 07:06
@github-actions
Copy link

This PR was marked as stale after 7 days of inactivity and will be closed after another 7 days of further inactivity. If this PR should be kept open, just add a comment, remove the stale label or push new commits to it.

@github-actions github-actions bot added the Stale PR is marked as stale due to inactivity. label Jan 16, 2026
@marceljk marceljk added has internal tracking issue ignore-stale and removed Stale PR is marked as stale due to inactivity. labels Jan 16, 2026
Copy link
Contributor

@cgoetz-inovex cgoetz-inovex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm besides the docs

MarkdownDescription: "Name of the type of the machine for the server. Possible values are documented in [Virtual machine flavors](https://docs.stackit.cloud/products/compute-engine/server/basics/machine-types/)",
Computed: true,
},
"agent": schema.SingleNestedAttribute{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot run the acceptance tests (if I do, it probably costs money). I did not want to add code to files I cannot test.
If this is a strong requirement for this PR, how can I do this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is, we won't accept this without properly adjusted Acc tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're working at STACKIT it shouldn't be a problem for you I hope? 😅

Copy link
Contributor Author

@aeter aeter Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like you should be using go-vcr or similar for such integration tests - recording/hardcoding API responses. The way they are written right now - it seems like they belong to an internal CI server, maybe even at the repo of a QA team. stackit-cli doesn't have them. The tests themselves and surely are useful - but how can you ask open source contributors to run them? How much does a sample run cost?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you have reached an agreement of a right implementation, I'll update/run the acceptance tests and update the PR.

Attributes: map[string]schema.Attribute{
"provisioned": schema.BoolAttribute{
Description: "Whether a STACKIT Server Agent is provisioned at the server",
Computed: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's what the API docs say about the "provisioned" server agent field: https://docs.api.stackit.cloud/documentation/iaas/version/v2#tag/Servers/operation/v2CreateServer

When false the agent IS NOT installed. When true the agent IS installed. When its not set the result depend on the used image and its default provisioning setting.

Since this is not trivial to solve, we as the STACKIT Developer Tools team decided that the default value for the "provisioned" field should be false. Could you please adjust your implementation to respect that decision? 😅

Copy link
Contributor Author

@aeter aeter Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be sure I understand it right:

  • By the previous code, so far, the param always has defaulted to null. (I think) I keep it as it has always been. Like - don't send any agent-related param to the API.
  • Currently (by my code) the param has 3 states: null, false, true. Default remains null.

It seems like you want to be even more explicit - and to always default the param to false, without a null state (thus - to not use any image default agent provisioning) .
OK. I'll update the code (cannot do it today, I also need to re-test it all).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a product owner of STACKIT Server Agent, I disagree with this to make the agent provisioned false by default.

Please note that agent is also a prerequisite to use the server update management service. If we put it by default false, I am sure that we will need to answer on a lot of customer support tickets, why the updates are not working. (other features like basic server monitoring are also planned to work only via the agent).
From other point of view there are some concerns to make the agent provisioning always true as on custom images this might not be expected.

There was a long discussion last year about the default value for agent installation on different levels (even up to the patrons). The final decision which was made is that it is the best is to have the default property configured on the image. This means that on publicly provided by STACKIT images it will be true and for custom images the customers can decide what to do.

I agree on this that image resource should return also the agent properties to make it more transparent, but we shouldn't have a default value about agent provisioning on server level.

The behavior of this property should be consistent through all available options to create a server - API, Portal, Terraform provider, CLI. That is why please ensure that it follows the already agreed behavior:

  • no default value for the agent on server level
  • if there is no value provided, use the value from the API (UseStateForUnknown)
  • agent provisioning property is read only, so if you change it, the server need to be recreated (RequiresReplace)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@btotev-schwarz We decided to use a default value (false) as the Developer Tools team and maintainers of this Terraform provider for the following reason: The API in it's current state is not declarative-friendly.

If one decides to not send a "provisioned" flag for a server POST request, the "provisioned" flag in the response can either be true or false in the response, but you can't know beforehand. This is not at all compatible with declarative tools like Terraform IMO. Making this work in Terraform will always require some kind of workarounds and I'm not willing to accept this.

  • agent provisioning property is read only, so if you change it, the server need to be recreated (RequiresReplace)

Don't mix up read-only fields and fields marked as RequiresReplace. agent.provisioned can't be read-only because the user has to be able to set a value for it in his Terraform configuration. But it has to be marked as RequiresReplace because the server update endpoint doesn't allow updating the "provisioned" flag of an existing server.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, you'll think I'm stubborn to continue the discussion and propose some technical solution. I know my code in the PR needs adjustments. I know the IaaS API itself probably could use a better design for this parameter (to use strings, like "yes", "no", "image-default" instead of a bool). I'll follow whatever you guys decide (product owners, terraform team).

A small question: is the below solution (keeping the current API model) harder to maintain for the team and harder/unintuitive for the users? Sorry from taking your time with it.

 // If the user omits "agent" and the API returns nothing, Terraform sees null in the config and null in the state. No changes are detected.
// If the API returns {"provisioned": true}, UseStateForUnknown() will copy that object into the plan. Terraform will respect the API's default.
    return schema.Schema{
        Attributes: map[string]schema.Attribute{
            "agent": schema.SingleNestedAttribute{
                // allow user to omit it, or API to set it.
                Optional: true,
                Computed: true,
                
                // If user omits it, keep the old value
                PlanModifiers: []planmodifier.Object{
                    objectplanmodifier.UseStateForUnknown(),
                },

                Attributes: map[string]schema.Attribute{
                    "provisioned": schema.BoolAttribute{
                        Optional: true, // Can be set 
                        Computed: true, // But the API might fill it too.
                        
                        // If user changes this value, force recreation (Immutable behavior)
                        PlanModifiers: []planmodifier.Bool{
                            boolplanmodifier.RequiresReplace(),
                        },
                    },
                },
            },
        },
    } 

It seems the "provisioned" flag is not returned from the API at all when the agent: provisioned: ... was not set during image creation. The above will work whether nothing is returned or true/false is returned.

[~/stackit-cli] stackit curl -X GET -H "Content-Type: application/json" https://iaas.api.stackit.cloud/v
2/projects/c904f41c-2f8c-4edb-b966-e87d65f10b64/regions/eu01/servers/5c013c9e-edc0-49a0-ba11-3c971f113bd
5
{
  "availabilityZone": "eu01-m",
  "bootVolume": {
    "deleteOnTermination": true,
    "id": "8af61d89-41cc-49cb-946c-bc75e0cb74c6"
  },
  "createdAt": "2026-01-22T10:30:10Z",
  "id": "5c013c9e-edc0-49a0-ba11-3c971f113bd5",
  "labels": {},
  "launchedAt": "2026-01-22T10:31:37Z",
  "machineType": "t1.1",
  "metadata": {},
  "name": "test2",
  "powerStatus": "RUNNING",
  "status": "ACTIVE",
  "updatedAt": "2026-01-22T10:31:37Z",
  "volumes": [
    "8af61d89-41cc-49cb-946c-bc75e0cb74c6"
  ]
}

If you're interested, I can provide further testing with creating a resource with the current code and modifying it (true/false/missing). Sorry for the interruption.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rubenhoenle

Is UseStateForUnknown() a workaround or officially supported plan modifier for such cases (https://developer.hashicorp.com/terraform/plugin/framework/resources/plan-modification#usestateforunknown)?

I mean readonly from server point of view - that you can't modify after that for the same resource via API. Of course you should be able to change this in the terraform and it will replace the resource.

Adds a terraform `stackit_server` option for the iaas ( _create server_ ) API param:
`"agent": {"provisioned": true}`

ref STACKITRCO-187

ref: stackitcloud/stackit-cli#1213

---

Tests:
* ran `make fmt`, `make generate-docs`

* ran unit tests
```
[~/terraform-provider-stackit] go test stackit/internal/services/iaas/server/*
ok      command-line-arguments  15.005s
[~/terraform-provider-stackit] make test
...
ok      github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/server       15.006s  coverage: 33.0% of statements
...
[~/terraform-provider-stackit]
```

* Tested: with a locally-configured terraform - tested by adding, changing, deleting `agent`-related parts of the below main.tf
** Tested without providing agent inside main.tf (the result had `"agent" = null /* object */`)
** Tested with setting `agent = { provisioning = true }` (and then ran the `stackit-cli` command for checking if the agent was created successfully and able to run commands, `stackit -y server command create --server-id=3fdac6ea-3885-441c-b473-bc94ca570ca8 --project-id=c904f41c-2f8c-4edb-b966-e87d65f10b64 --template-name=RunShellScript --params script='echo hello'`).
** Tested when setting `agent = { provisioning = true }` and then set it to false - verified the server was deleted and recreated again with the new value.

```
[~] cat main.tf
provider "stackit" {
  # Configuration options
  service_account_key_path = "/home/debian/terraform_dev/.terraform_key.json"
  default_region = "eu01"
}

resource "stackit_network_interface" "server_nic" {
  project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64"
  network_id = "97c5dde4-cb9d-49b8-be55-9cdf0c3795e1"
}

resource "stackit_server" "myserver1" {
    project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64"
    name = "terraformtestserver1"
    boot_volume = {
        size = 64
        source_type = "image"
        source_id = "21466190-b904-4267-8bf3-1be4323f4ffb"
        delete_on_termination = true
    }
    availability_zone = "eu01-1"
    agent = {
        provisioned = true
    }
    machine_type = "t1.1"
    network_interfaces = [ stackit_network_interface.server_nic.network_interface_id ]
}

data "stackit_server" "myserver1_data" {
   project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64"
   server_id  = stackit_server.myserver1.server_id
}

output "server_info_from_data" {
  value = data.stackit_server.myserver1_data
}
```

```
[~] terraform apply -auto-approve  ## this is without `agent` set in the config
...
server_info_from_data = {
  "affinity_group" = tostring(null)
  "agent" = null /* object */
  "availability_zone" = "eu01-1"
  ...
}
...
```

```
[~] terraform apply -auto-approve  ## this is with `agent = { provisioned = true }` set in the config
...
server_info_from_data = {
  "affinity_group" = tostring(null)
  "agent" = {
    "provisioned" = true
  }
  "availability_zone" = "eu01-1"
  "boot_volume" = {
    "delete_on_termination" = true
    "id" = "673021e5-2a90-4482-8ffa-e0485c7588bd"
  }
  ...
}
```

Signed-off-by: Adrian Nackov <adrian.nackov@mail.schwarz>
@aeter aeter force-pushed the feature/STACKITRCO-187-iaas-create-server-agent-param branch from d208dba to 91a621b Compare January 21, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants