Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/resources/instance_server.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ attached to the server. Updates to this field will trigger a stop/start of the s

- `protected` - (Optional) Set to true to activate server protection option.

- `admin_password_encryption_ssh_key_id` - (Optional) The ID of the SSH RSA key that will be used to encrypt the initial admin password for OS requiring it.
Mandatory for Windows OS. The public_key value of this key is used to encrypt the admin password.
When set to an empty string, it resets this value and admin_password_encrypted_value to an empty string so a new password may be generated.

- `zone` - (Defaults to [provider](../index.md#zone) `zone`) The [zone](../guides/regions_and_zones.md#zones) in which the server should be created.

- `project_id` - (Defaults to [provider](../index.md#project_id) `project_id`) The ID of the project the server is associated with.
Expand Down
18 changes: 18 additions & 0 deletions internal/services/instance/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,12 @@ func ResourceServer() *schema.Resource {
},
},
},
"admin_password_encryption_ssh_key_id": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: verify.IsUUIDOrEmpty(),
Description: "The ID of the IAM SSH key used to encrypt the initial admin password on a Windows server",
},
"zone": zonal.Schema(),
"organization_id": account.OrganizationIDSchema(),
"project_id": account.ProjectIDSchema(),
Expand Down Expand Up @@ -439,6 +445,10 @@ func ResourceInstanceServerCreate(ctx context.Context, d *schema.ResourceData, m
req.PlacementGroup = types.ExpandStringPtr(zonal.ExpandID(placementGroupID).ID)
}

if adminPasswordEncryptionSSHKeyID, ok := d.GetOk("admin_password_encryption_key_ssh_id"); ok {
req.AdminPasswordEncryptionSSHKeyID = types.ExpandStringPtr(adminPasswordEncryptionSSHKeyID)
}

serverType := getServerType(ctx, api.API, req.Zone, req.CommercialType)
if serverType == nil {
return diag.Diagnostics{{
Expand Down Expand Up @@ -715,6 +725,10 @@ func ResourceInstanceServerRead(ctx context.Context, d *schema.ResourceData, m a
_ = d.Set("ipv6_prefix_length", nil)
}

if server.AdminPasswordEncryptionSSHKeyID != nil {
_ = d.Set("admin_password_encryption_ssh_key_id", server.AdminPasswordEncryptionSSHKeyID)
}

var additionalVolumesIDs []string

for i, serverVolume := range sortVolumeServer(server.Volumes) {
Expand Down Expand Up @@ -973,6 +987,10 @@ func ResourceInstanceServerUpdate(ctx context.Context, d *schema.ResourceData, m
}
}

if d.HasChange("admin_password_encryption_ssh_key_id") {
updateRequest.AdminPasswordEncryptionSSHKeyID = types.ExpandUpdatedStringPtr(d.Get("admin_password_encryption_ssh_key_id").(string))
}

////
// Update reserved IP
////
Expand Down
73 changes: 73 additions & 0 deletions internal/services/instance/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/zonal"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
iamchecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/iam/testfuncs"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/instance"
instancechecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/instance/testfuncs"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -2097,6 +2098,78 @@ func TestAccServer_PrivateNetworkMissingPNIC(t *testing.T) {
})
}

func TestAccServer_AdminPasswordEncryptionSSHKeyID(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()

sshKey := "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEEYrzDOZmhItdKaDAEqJQ4ORS2GyBMtBozYsK5kiXXX [email protected]"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
instancechecks.IsServerDestroyed(tt),
iamchecks.CheckSSHKeyDestroy(tt),
),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "scaleway_iam_ssh_key" "main" {
name = "test-acc-admin-pwd-encryption"
public_key = %q
}

resource "scaleway_instance_server" "main" {
type = "POP2-2C-8G-WIN"
image = "windows_server_2022"
admin_password_encryption_ssh_key_id = scaleway_iam_ssh_key.main.id
}
`, sshKey),
Check: resource.ComposeTestCheckFunc(
iamchecks.CheckSSHKeyExists(tt, "scaleway_iam_ssh_key.main"),
resource.TestCheckResourceAttr("scaleway_instance_server.main", "type", "POP2-2C-8G-WIN"),
resource.TestCheckResourceAttr("scaleway_instance_server.main", "image", "windows_server_2022"),
resource.TestCheckResourceAttrPair("scaleway_instance_server.main", "admin_password_encryption_ssh_key_id", "scaleway_iam_ssh_key.main", "id"),
),
},
{
Config: fmt.Sprintf(`
resource "scaleway_iam_ssh_key" "main" {
name = "test-acc-admin-pwd-encryption"
public_key = %q
}

resource "scaleway_instance_server" "main" {
type = "POP2-2C-8G-WIN"
image = "windows_server_2022"
admin_password_encryption_ssh_key_id = ""
}
`, sshKey),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("scaleway_instance_server.main", "admin_password_encryption_ssh_key_id", ""),
),
},
{
Config: fmt.Sprintf(`
resource "scaleway_iam_ssh_key" "main" {
name = "test-acc-admin-pwd-encryption"
public_key = %q
}

resource "scaleway_instance_server" "main" {
type = "POP2-2C-8G-WIN"
image = "windows_server_2022"
admin_password_encryption_ssh_key_id = scaleway_iam_ssh_key.main.id
}
`, sshKey),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair("scaleway_instance_server.main", "admin_password_encryption_ssh_key_id", "scaleway_iam_ssh_key.main", "id"),
),
},
},
})
}

func TestGetEndOfServiceDate(t *testing.T) {
tt := acctest.NewTestTools(t)
client := meta.ExtractScwClient(tt.Meta)
Expand Down

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions internal/verify/uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,34 @@ func IsUUID() schema.SchemaValidateDiagFunc {
}
}

func IsUUIDOrEmpty() schema.SchemaValidateDiagFunc {
return func(value any, path cty.Path) diag.Diagnostics {
uuid, isString := value.(string)
if !isString {
return diag.Diagnostics{diag.Diagnostic{
Severity: diag.Error,
Summary: "invalid UUID not a string",
AttributePath: path,
}}
}

if uuid == "" {
return nil
}

if !validation.IsUUID(uuid) {
return diag.Diagnostics{diag.Diagnostic{
Severity: diag.Error,
Summary: "invalid UUID: " + uuid,
AttributePath: path,
Detail: "format should be 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' (36) and contains valid hexadecimal characters",
}}
}

return nil
}
}

func IsUUIDWithLocality() schema.SchemaValidateDiagFunc {
return func(value any, path cty.Path) diag.Diagnostics {
uuid, isString := value.(string)
Expand Down
Loading