Skip to content

Commit

Permalink
Merge pull request #34266 from DanielRieske/f/add-appstream-multi-ses…
Browse files Browse the repository at this point in the history
…sion-support

Added multi-session support on the `appstream_fleet` resource
  • Loading branch information
jar-b authored Apr 2, 2024
2 parents cd748df + ff09dc6 commit a6381d5
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 4 deletions.
7 changes: 7 additions & 0 deletions .changelog/34266.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_appstream_fleet: Add `desired_sessions` argument to the `compute_capacity` block.
```

```release-note:enhancement
resource/aws_appstream_fleet: Add `max_sessions_per_instance` argument.
```
41 changes: 38 additions & 3 deletions internal/service/appstream/fleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,17 @@ func ResourceFleet() *schema.Resource {
},
"desired_instances": {
Type: schema.TypeInt,
Required: true,
Optional: true,
ExactlyOneOf: []string{
"compute_capacity.0.desired_sessions",
},
},
"desired_sessions": {
Type: schema.TypeInt,
Optional: true,
ExactlyOneOf: []string{
"compute_capacity.0.desired_instances",
},
},
"in_use": {
Type: schema.TypeInt,
Expand Down Expand Up @@ -155,6 +165,10 @@ func ResourceFleet() *schema.Resource {
Type: schema.TypeString,
Required: true,
},
"max_sessions_per_instance": {
Type: schema.TypeInt,
Optional: true,
},
"max_user_duration_in_seconds": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -255,6 +269,10 @@ func resourceFleetCreate(ctx context.Context, d *schema.ResourceData, meta inter
input.IamRoleArn = aws.String(v.(string))
}

if v, ok := d.GetOk("max_sessions_per_instance"); ok {
input.MaxSessionsPerInstance = aws.Int64(int64(v.(int)))
}

if v, ok := d.GetOk("max_user_duration_in_seconds"); ok {
input.MaxUserDurationInSeconds = aws.Int64(int64(v.(int)))
}
Expand Down Expand Up @@ -372,6 +390,7 @@ func resourceFleetRead(ctx context.Context, d *schema.ResourceData, meta interfa
d.Set("image_name", fleet.ImageName)
d.Set("image_arn", fleet.ImageArn)
d.Set("instance_type", fleet.InstanceType)
d.Set("max_sessions_per_instance", fleet.MaxSessionsPerInstance)
d.Set("max_user_duration_in_seconds", fleet.MaxUserDurationInSeconds)
d.Set("name", fleet.Name)
d.Set("state", fleet.State)
Expand Down Expand Up @@ -462,6 +481,10 @@ func resourceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta inter
input.InstanceType = aws.String(d.Get("instance_type").(string))
}

if d.HasChange("max_sessions_per_instance") {
input.MaxSessionsPerInstance = aws.Int64(int64(d.Get("max_sessions_per_instance").(int)))
}

if d.HasChange("max_user_duration_in_seconds") {
input.MaxUserDurationInSeconds = aws.Int64(int64(d.Get("max_user_duration_in_seconds").(int)))
}
Expand Down Expand Up @@ -502,6 +525,9 @@ func resourceFleetDelete(ctx context.Context, d *schema.ResourceData, meta inter
_, err := conn.StopFleetWithContext(ctx, &appstream.StopFleetInput{
Name: aws.String(d.Id()),
})
if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) {
return diags
}
if err != nil {
return sdkdiag.AppendErrorf(diags, "stopping Appstream Fleet (%s): %s", d.Id(), err)
}
Expand Down Expand Up @@ -545,10 +571,14 @@ func expandComputeCapacity(tfList []interface{}) *appstream.ComputeCapacity {
apiObject := &appstream.ComputeCapacity{}

attr := tfList[0].(map[string]interface{})
if v, ok := attr["desired_instances"]; ok {
if v, ok := attr["desired_instances"]; ok && v != 0 {
apiObject.DesiredInstances = aws.Int64(int64(v.(int)))
}

if v, ok := attr["desired_sessions"]; ok && v != 0 {
apiObject.DesiredSessions = aws.Int64(int64(v.(int)))
}

if reflect.DeepEqual(&appstream.ComputeCapacity{}, apiObject) {
return nil
}
Expand All @@ -563,7 +593,12 @@ func flattenComputeCapacity(apiObject *appstream.ComputeCapacityStatus) map[stri

tfMap := map[string]interface{}{}

if v := apiObject.Desired; v != nil {
if v := apiObject.DesiredUserSessions; v != nil {
tfMap["desired_sessions"] = aws.Int64Value(v)
}

// desiredInstances is always returned by the API but cannot be used in conjunction with desiredSessions
if v := apiObject.Desired; v != nil && tfMap["desired_sessions"] == nil {
tfMap["desired_instances"] = aws.Int64Value(v)
}

Expand Down
91 changes: 91 additions & 0 deletions internal/service/appstream/fleet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,55 @@ func TestAccAppStreamFleet_emptyDomainJoin(t *testing.T) {
})
}

func TestAccAppStreamFleet_multiSession(t *testing.T) {
ctx := acctest.Context(t)
var fleetOutput appstream.Fleet
resourceName := "aws_appstream_fleet.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
instanceType := "stream.standard.small"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckHasIAMRole(ctx, t, "AmazonAppStreamServiceAccess")
},
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckFleetDestroy(ctx),
ErrorCheck: acctest.ErrorCheck(t, names.AppStreamServiceID),
Steps: []resource.TestStep{
{
Config: testAccFleetConfig_multiSession(rName, instanceType, 1, 5),
Check: resource.ComposeTestCheckFunc(
testAccCheckFleetExists(ctx, resourceName, &fleetOutput),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType),
resource.TestCheckResourceAttr(resourceName, "max_sessions_per_instance", "5"),
resource.TestCheckResourceAttr(resourceName, "compute_capacity.0.desired_sessions", "1"),
resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning),
acctest.CheckResourceAttrRFC3339(resourceName, "created_time"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccFleetConfig_multiSession(rName, instanceType, 2, 10),
Check: resource.ComposeTestCheckFunc(
testAccCheckFleetExists(ctx, resourceName, &fleetOutput),
resource.TestCheckResourceAttr(resourceName, "name", rName),
resource.TestCheckResourceAttr(resourceName, "instance_type", instanceType),
resource.TestCheckResourceAttr(resourceName, "max_sessions_per_instance", "10"),
resource.TestCheckResourceAttr(resourceName, "compute_capacity.0.desired_sessions", "2"),
resource.TestCheckResourceAttr(resourceName, "state", appstream.FleetStateRunning),
acctest.CheckResourceAttrRFC3339(resourceName, "created_time"),
),
},
},
})
}

func testAccCheckFleetExists(ctx context.Context, resourceName string, appStreamFleet *appstream.Fleet) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand Down Expand Up @@ -516,3 +565,45 @@ resource "aws_appstream_fleet" "test" {
}
`, name, instanceType, empty)
}

func testAccFleetConfig_multiSession(name, instanceType string, desiredSessions, maxSessionsPerInstance int) string {
return acctest.ConfigCompose(
acctest.ConfigAvailableAZsNoOptIn(),
fmt.Sprintf(`
data "aws_region" "current" {}
data "aws_partition" "current" {}
resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "test" {
count = 2
availability_zone = data.aws_availability_zones.available.names[count.index]
cidr_block = "10.0.${count.index}.0/24"
vpc_id = aws_vpc.test.id
}
resource "aws_appstream_fleet" "test" {
name = %[1]q
image_arn = "arn:${data.aws_partition.current.partition}:appstream:${data.aws_region.current.name}::image/AppStream-WinServer2019-01-26-2024"
compute_capacity {
desired_sessions = %[3]d
}
description = "Description for a multi-session fleet"
idle_disconnect_timeout_in_seconds = 70
enable_default_internet_access = false
fleet_type = "ON_DEMAND"
instance_type = %[2]q
max_sessions_per_instance = %[4]d
max_user_duration_in_seconds = 1000
stream_view = "DESKTOP"
vpc_config {
subnet_ids = aws_subnet.test[*].id
}
}
`, name, instanceType, desiredSessions, maxSessionsPerInstance))
}
6 changes: 5 additions & 1 deletion website/docs/r/appstream_fleet.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,17 @@ The following arguments are optional:
* `image_name` - (Optional) Name of the image used to create the fleet.
* `image_arn` - (Optional) ARN of the public, private, or shared image to use.
* `stream_view` - (Optional) AppStream 2.0 view that is displayed to your users when they stream from the fleet. When `APP` is specified, only the windows of applications opened by users display. When `DESKTOP` is specified, the standard desktop that is provided by the operating system displays. If not specified, defaults to `APP`.
* `max_sessions_per_instance` - (Optional) The maximum number of user sessions on an instance. This only applies to multi-session fleets.
* `max_user_duration_in_seconds` - (Optional) Maximum amount of time that a streaming session can remain active, in seconds.
* `vpc_config` - (Optional) Configuration block for the VPC configuration for the image builder. See below.
* `tags` - (Optional) Map of tags to attach to AppStream instances.

### `compute_capacity`

* `desired_instances` - (Required) Desired number of streaming instances.
Exactly one of `desired_instances` or `desired_sessions` must be set, based on the type of fleet being created.

* `desired_instances` - (Optional) Desired number of streaming instances.
* `desired_sessions` - (Optional) Desired number of user sessions for a multi-session fleet. This is not allowed for single-session fleets.

### `domain_join_info`

Expand Down

0 comments on commit a6381d5

Please sign in to comment.