diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100755 index 0000000..b94204d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,51 @@ +# This GitHub action can publish assets for release when a tag is created. +# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). +# +# This uses an action (paultyng/ghaction-import-gpg) that assumes you set your +# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `PASSPHRASE` +# secret. If you would rather own your own GPG handling, please fork this action +# or use an alternative one for key handling. +# +# You will need to pass the `--batch` flag to `gpg` in your signing step +# in `goreleaser` to indicate this is being used in a non-interactive mode. +# +name: release +on: + push: + tags: + - 'v*' +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2.3.4 + - + name: Unshallow + run: git fetch --prune --unshallow + - + name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: 1.16 + - + name: Import GPG key + id: import_gpg + # TODO: move this to HashiCorp namespace or find alternative that is just simple gpg commands + # see https://github.com/hashicorp/terraform-provider-scaffolding/issues/22 + uses: hashicorp/ghaction-import-gpg@v2.1.0 + env: + # These secrets will need to be configured for the repository: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + PASSPHRASE: ${{ secrets.PASSPHRASE }} + - + name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2.5.0 + with: + version: latest + args: release --rm-dist + env: + GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} + # GitHub sets this automatically + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 4033a26..5c540cf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ example.tf terraform.tfplan terraform.tfstate +terraform.tfvars +dist/ vendor/ bin/ modules-dev/ @@ -39,4 +41,4 @@ website/vendor node_modules -go.sum \ No newline at end of file +go.sum diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..6552a85 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,54 @@ +# Visit https://goreleaser.com for documentation on how to customize this +# behavior. +before: + hooks: + # this is just an example and not a requirement for provider building/publishing + - go mod tidy +builds: +- env: + # goreleaser does not work with CGO, it could also complicate + # usage by users in CI/CD systems like Terraform Cloud where + # they are unable to install libraries. + - CGO_ENABLED=0 + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - '386' + - arm + - arm64 + ignore: + - goos: darwin + goarch: '386' + binary: '{{ .ProjectName }}_v{{ .Version }}' +archives: +- format: zip + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' +checksum: + name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' + algorithm: sha256 +signs: + - artifacts: checksum + args: + # if you are using this in a GitHub action or some other automated pipeline, you + # need to pass the batch flag to indicate its not interactive. + - "--batch" + - "--local-user" + - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key + - "--output" + - "${signature}" + - "--detach-sign" + - "${artifact}" +release: + # If you want to manually examine the release before its live, uncomment this line: + draft: true +changelog: + skip: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d681bf..13ed525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +## WIP (**UNRELEASED**) + +FEATURES: +* [nparfait] appstream/resource_fleet.go - state, stream_view +* [nparfait] appstream/resource_image_builder.go - access_endpoints +* [nparfait] appstream/resource_stack.go - access_endpoints, application_settings +* [julian3xl] appstream/resource_stack.go - embedded_domain +* [julian3xl] appstream/resource_stack_attachment.go - new resource +* [nparfait] appstream/appstream_usage_report_subscription.go - new resource + +ENHANCEMENTS: +* [arnvid] upgraded go modules +* [arnvid] upgraded go api, sdk and interfaces +* [arnvid] appstream/provider.go - support for longer session times +* [julian3xl] appstream/resource_fleet.go - iam_role_arn -> optional +* [julian3xl] appstream/resource_fleet.go - added support for account local image_name +* [julian3xl] appstream/resource_fleet.go - update to vpc_config variable +* [julian3xl] appstream/resource_fleet.go - updated tagging +* [julian3xl] appstream/resource_fleet.go - cleaned up comments and errorlogs +* [julian3xl] appstream/resource_fleet.go - added idle_disconnect_timeout & enable_default_internet_access +* [julian3xl] appstream/resource_stack.go - updated user_settings. +* [other] appstream/resource_stack.go - multiple fixes + +BUGFIXES: +* [suhussai] search for stack, fleet and image builder by name instead of listing all #5 +* [julian3xl] api inconsistencies +* [arnvid] bug in optional variables forcing recreation + +TOOLS: +* [nparfait] Added support for automated builds actions github actions +* [canha] some fixes to automated build tools for newer go lang + ## 1.0.8 (June 15, 2020) FEATURES: diff --git a/README.md b/README.md index 1c5bb17..9e8ff3c 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,23 @@ # terraform-provider-appstream + + ++This is an updated Terraform Appstream provider that covers almost all AWS SDK operations on Appstream service. This fork attempts to build a suitable provider that conforms as clossely as possible terraform provider standards. + + # Provider usage ``` -$ go build -o terraform-provider-appstream +$ go build ~/.terraform.d/plugins/terraform-provider-appstream (for terraform 0.12) +$ go build ~/.terraform.d/plugin-cache/registry.terraform.io/arnvid/appstream/1.0.x/ (for terraform 0.13+) $ terraform init $ terraform plan $ terraform apply ``` #Development notes -Several other terraform provider projects have been used to refrence how a module should be written, +Several other terraform provider projects have been used to reference how a module should be written, The goal of this version is to be able to run properly with Terraform Cloud and Terraform Enterprise. Along side with removing the need for access and secret key in variables and only pass the necessary to be assumed. @@ -20,13 +26,14 @@ to be assumed. Large portions of code for authentication in config.go & provider.go is from: https://github.com/terraform-providers/terraform-provider-aws - - - ## Authors/Contributors/Forks This fork is by: [Arnvid Karstad](https://github.com/arnvid) +Contributors: +[Konstantin Odnoralov](https://github.com/hostmit) +[Syed Hussain](https://github.com/suhussai) + Original code from: https://github.com/ops-guru/terraform-provider-appstream [Viktor Berlov](https://github.com/vktr-brlv) @@ -34,3 +41,13 @@ https://github.com/ops-guru/terraform-provider-appstream Other forks ref'd: https://github.com/bluesentry/terraform-provider-appstream [Chris Mackubin](https://github.com/chris-mackubin) + +https://github.com/nparfait/terraform-provider-appstream +[Nicolas Parfait](https://github.com/nparfait) + +https://github.com/julian3xl/terraform-provider-appstream +[julian3xl](https://github.com/julian3xl) + +https://github.com/PMI-DEEP-Infra/terraform-provider-appstream +[PMI DEEP Infra Team](https://github.com/PMI-DEEP-Infra) +[Jorge Canha](jorge.canha@pmi.com) diff --git a/appstream/config.go b/appstream/config.go index 3aba457..4682d14 100644 --- a/appstream/config.go +++ b/appstream/config.go @@ -1,47 +1,48 @@ package appstream import ( + "fmt" + "log" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/service/appstream" "github.com/aws/aws-sdk-go/service/imagebuilder" awsbase "github.com/hashicorp/aws-sdk-go-base" - "github.com/hashicorp/terraform-plugin-sdk/helper/logging" - "fmt" - "log" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" ) - type Config struct { - AccessKey string - SecretKey string - CredsFilename string - Profile string - Token string - Region string - MaxRetries int - - AssumeRoleARN string - AssumeRoleExternalID string - AssumeRoleSessionName string - AssumeRolePolicy string - - AllowedAccountIds []string - ForbiddenAccountIds []string - - Endpoints map[string]string + AccessKey string + SecretKey string + CredsFilename string + Profile string + Token string + Region string + MaxRetries int + + AssumeRoleARN string + AssumeRoleDurationSeconds int + AssumeRoleExternalID string + AssumeRoleSessionName string + AssumeRolePolicy string + + AllowedAccountIds []string + ForbiddenAccountIds []string + + Endpoints map[string]string IgnoreTagPrefixes []string IgnoreTags []string - Insecure bool + Insecure bool - SkipCredsValidation bool - SkipGetEC2Platforms bool - SkipRegionValidation bool - SkipRequestingAccountId bool - SkipMetadataApiCheck bool - S3ForcePathStyle bool + SkipCredsValidation bool + SkipGetEC2Platforms bool + SkipRegionValidation bool + SkipRequestingAccountId bool + SkipMetadataApiCheck bool + S3ForcePathStyle bool - terraformVersion string + terraformVersion string } type AWSClient struct { @@ -119,14 +120,14 @@ func (c *Config) Client() (interface{}, error) { dnsSuffix := "amazonaws.com" if p, ok := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), c.Region); ok { dnsSuffix = p.DNSSuffix() - } - + } + client := &AWSClient{ accountid: accountID, appstreamconn: appstream.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["appstream"])})), - dnsSuffix: dnsSuffix, + dnsSuffix: dnsSuffix, imagebuilderconn: imagebuilder.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["imagebuilder"])})), - partition: partition, + partition: partition, region: c.Region, terraformVersion: c.terraformVersion, } diff --git a/appstream/provider.go b/appstream/provider.go index 7ea1f60..410a928 100644 --- a/appstream/provider.go +++ b/appstream/provider.go @@ -1,14 +1,13 @@ package appstream import ( - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - homedir "github.com/mitchellh/go-homedir" "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + homedir "github.com/mitchellh/go-homedir" ) -func Provider() terraform.ResourceProvider { - +func Provider() *schema.Provider { provider := &schema.Provider{ Schema: map[string]*schema.Schema{ "access_key": { @@ -61,9 +60,11 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "appstream_stack": resourceAppstreamStack(), - "appstream_image_builder": resourceAppstreamImageBuilder(), - "appstream_fleet": resourceAppstreamFleet(), + "appstream_stack": resourceAppstreamStack(), + "appstream_image_builder": resourceAppstreamImageBuilder(), + "appstream_stack_attachment": resourceAppstreamStackAttachment(), + "appstream_fleet": resourceAppstreamFleet(), + "appstream_usage_report_subscription": resourceAppstreamUsageReportSubscription(), }, } provider.ConfigureFunc = func(d *schema.ResourceData) (interface{}, error) { @@ -100,7 +101,7 @@ func init() { "token": "session token. A session token is only required if you are\n" + "using temporary security credentials.", - "max_retries": "The maximum number of times an AWS API request is\n" + + "max_retries": "The maximum number of times an AWS API request is\n" + "being executed. If the API request still fails, an error is\n" + "thrown.", @@ -137,18 +138,26 @@ func init() { "assume_role_external_id": "The external ID to use when assuming the role. If omitted," + " no external ID is passed to the AssumeRole call.", + "assume_role_duration_seconds": "The duration, in seconds, of the role session. " + + "The value specified can can range from 900 seconds (15 minutes) " + + "up to the maximum session duration that is set for the role. " + + "The maximum session duration setting can have a value from 1 hour to 12 hours. " + + "If you specify a value higher than this setting, the operation fails. For example, " + + "if you specify a session duration of 12 hours, but your administrator set the maximum " + + " session duration to 6 hours, your operation fails.", + "assume_role_policy": "The permissions applied when assuming a role. You cannot use," + " this policy to grant further permissions that are in excess to those of the, " + " role that is being assumed.", } } func providerConfigure(d *schema.ResourceData, terraformVersion string) (interface{}, error) { - config := Config { - AccessKey: d.Get("access_key").(string), - SecretKey: d.Get("secret_key").(string), - Profile: d.Get("profile").(string), - Token: d.Get("token").(string), - Region: d.Get("region").(string), + config := Config{ + AccessKey: d.Get("access_key").(string), + SecretKey: d.Get("secret_key").(string), + Profile: d.Get("profile").(string), + Token: d.Get("token").(string), + Region: d.Get("region").(string), terraformVersion: terraformVersion, } @@ -165,7 +174,11 @@ func providerConfigure(d *schema.ResourceData, terraformVersion string) (interfa config.AssumeRoleARN = assumeRole["role_arn"].(string) config.AssumeRoleSessionName = assumeRole["session_name"].(string) config.AssumeRoleExternalID = assumeRole["external_id"].(string) - + if v := assumeRole["duration_seconds"].(int); v != 0 { + config.AssumeRoleDurationSeconds = v + } else { + config.AssumeRoleDurationSeconds = 1800 // default to 30 mins not 15.. + } if v := assumeRole["policy"].(string); v != "" { config.AssumeRolePolicy = v } @@ -197,6 +210,12 @@ func assumeRoleSchema() *schema.Schema { Description: descriptions["assume_role_session_name"], }, + "duration_seconds": { + Type: schema.TypeInt, + Optional: true, + Description: descriptions["assume_role_duration_seconds"], + }, + "external_id": { Type: schema.TypeString, Optional: true, diff --git a/appstream/resource_fleet.go b/appstream/resource_fleet.go index cde83df..5a299d8 100644 --- a/appstream/resource_fleet.go +++ b/appstream/resource_fleet.go @@ -1,140 +1,156 @@ package appstream import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/appstream" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "log" + "log" "strings" "time" -) + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) func resourceAppstreamFleet() *schema.Resource { - return &schema.Resource { - Create: resourceAppstreamFleetCreate, - Read: resourceAppstreamFleetRead, - Update: resourceAppstreamFleetUpdate, - Delete: resourceAppstreamFleetDelete, - Importer: &schema.ResourceImporter { - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "compute_capacity": { - Type: schema.TypeList, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "desired_instances": { - Type: schema.TypeInt, - Required: true, - }, - }, - }, - }, - - "description": { - Type: schema.TypeString, - Optional: true, - }, - - "disconnect_timeout": { - Type: schema.TypeInt, - Optional: true, - }, - - "display_name": { - Type: schema.TypeString, - Optional: true, - }, - - "domain_info": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "directory_name": { - Type: schema.TypeString, - Optional: true, - }, - "organizational_unit_distinguished_name": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - - "enable_default_internet_access": { - Type: schema.TypeBool, - Optional: true, - }, - - "fleet_type": { - Type: schema.TypeString, - Optional: true, - }, - + return &schema.Resource{ + Create: resourceAppstreamFleetCreate, + Read: resourceAppstreamFleetRead, + Update: resourceAppstreamFleetUpdate, + Delete: resourceAppstreamFleetDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "compute_capacity": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "desired_instances": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + }, + + "disconnect_timeout": { + Type: schema.TypeInt, + Optional: true, + }, + + "display_name": { + Type: schema.TypeString, + Optional: true, + }, + + "domain_info": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "directory_name": { + Type: schema.TypeString, + Optional: true, + }, + "organizational_unit_distinguished_name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + + "enable_default_internet_access": { + Type: schema.TypeBool, + Optional: true, + }, + + "fleet_type": { + Type: schema.TypeString, + Optional: true, + }, + "image_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"image_name"}, + }, + + "image_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"image_arn"}, }, - + "iam_role_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "instance_type": { - Type: schema.TypeString, - Required: true, - }, - - "max_user_duration": { - Type: schema.TypeInt, - Optional: true, - }, - - "name": { - Type: schema.TypeString, - Required: true, - }, - - "stack_name": { - Type: schema.TypeString, - Optional: true, - }, - - "state": { - Type: schema.TypeString, - Optional: true, - }, - - "vpc_config": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource { - Schema: map[string]*schema.Schema { - "security_group_ids": { - Type: schema.TypeString, - Optional: true, - }, - "subnet_ids": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "tags": { - Type: schema.TypeMap, - Optional: true, - }, - }, - } + Type: schema.TypeString, + Optional: true, + }, + + "instance_type": { + Type: schema.TypeString, + Required: true, + }, + + "max_user_duration": { + Type: schema.TypeInt, + Optional: true, + }, + + "name": { + Type: schema.TypeString, + Required: true, + }, + + "stack_name": { + Type: schema.TypeString, + Optional: true, + }, + + "state": { + Type: schema.TypeString, + Optional: true, + }, + + "stream_view": { + Type: schema.TypeString, + Optional: true, + Default: "APP", + }, + + "vpc_config": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_group_ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "subnet_ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + + "tags": { + Type: schema.TypeMap, + Optional: true, + }, + }, + } } func resourceAppstreamFleetCreate(d *schema.ResourceData, meta interface{}) error { @@ -151,13 +167,14 @@ func resourceAppstreamFleetCreate(d *schema.ResourceData, meta interface{}) erro if a, ok := d.GetOk("compute_capacity"); ok { ComputeAttributes := a.([]interface{}) attr := ComputeAttributes[0].(map[string]interface{}) + if v, ok := attr["desired_instances"]; ok { ComputeConfig.DesiredInstances = aws.Int64(int64(v.(int))) } + CreateFleetInputOpts.ComputeCapacity = ComputeConfig } - if v, ok := d.GetOk("description"); ok { CreateFleetInputOpts.Description = aws.String(v.(string)) } @@ -175,12 +192,15 @@ func resourceAppstreamFleetCreate(d *schema.ResourceData, meta interface{}) erro if dom, ok := d.GetOk("domain_info"); ok { DomainAttributes := dom.([]interface{}) attr := DomainAttributes[0].(map[string]interface{}) + if v, ok := attr["directory_name"]; ok { DomainJoinInfoConfig.DirectoryName = aws.String(v.(string)) } + if v, ok := attr["organizational_unit_distinguished_name"]; ok { DomainJoinInfoConfig.OrganizationalUnitDistinguishedName = aws.String(v.(string)) } + CreateFleetInputOpts.DomainJoinInfo = DomainJoinInfoConfig } @@ -196,10 +216,17 @@ func resourceAppstreamFleetCreate(d *schema.ResourceData, meta interface{}) erro CreateFleetInputOpts.ImageArn = aws.String(v.(string)) } + if v, ok := d.GetOk("image_name"); ok { + CreateFleetInputOpts.ImageName = aws.String(v.(string)) + } + if v, ok := d.GetOk("iam_role_arn"); ok { - CreateFleetInputOpts.IamRoleArn = aws.String(v.(string)) + CreateFleetInputOpts.IamRoleArn = aws.String(v.(string)) } + if v, ok := d.GetOk("idle_disconnect_timeout"); ok { + CreateFleetInputOpts.IdleDisconnectTimeoutInSeconds = aws.Int64(int64(v.(int))) + } if v, ok := d.GetOk("instance_type"); ok { CreateFleetInputOpts.InstanceType = aws.String(v.(string)) @@ -209,87 +236,49 @@ func resourceAppstreamFleetCreate(d *schema.ResourceData, meta interface{}) erro CreateFleetInputOpts.MaxUserDurationInSeconds = aws.Int64(int64(v.(int))) } - VpcConfigConfig := & appstream.VpcConfig{} + if v, ok := d.GetOk("stream_view"); ok { + CreateFleetInputOpts.StreamView = aws.String(v.(string)) + } - if vpc, ok := d.GetOk("vpc_config"); ok { - VpcAttributes := vpc.([]interface{}) - attr := VpcAttributes[0].(map[string]interface{}) - if v, ok := attr["security_group_ids"]; ok { - strSlice := strings.Split(v.(string), ",") - for i, s := range strSlice { - strSlice[i] = strings.TrimSpace(s) - } - VpcConfigConfig.SecurityGroupIds = aws.StringSlice(strSlice) - } - if v, ok := attr["subnet_ids"]; ok { - strSlice := strings.Split(v.(string), ",") - for i, s := range strSlice { - strSlice[i] = strings.TrimSpace(s) - } - VpcConfigConfig.SubnetIds = aws.StringSlice(strSlice) - } - CreateFleetInputOpts.VpcConfig = VpcConfigConfig - } + if v, ok := d.GetOk("vpc_config"); ok { + CreateFleetInputOpts.VpcConfig = expandVpcConfigs(v.([]interface{})) + } log.Printf("[DEBUG] Run configuration: %s", CreateFleetInputOpts) - resp, err := svc.CreateFleet(CreateFleetInputOpts) + resp, err := svc.CreateFleet(CreateFleetInputOpts) if err != nil { log.Printf("[ERROR] Error creating Appstream Fleet: %s", err) return err } - - log.Printf("[DEBUG] %s", resp) - time.Sleep(2 * time.Second) + log.Printf("[DEBUG] Appstream Fleet created %s ", resp) if v, ok := d.GetOk("tags"); ok { - - data_tags := v.(map[string]interface{}) - - attr := make(map[string]string) - - for k, v := range data_tags { - attr[k] = v.(string) - } - - tags := aws.StringMap(attr) + time.Sleep(2 * time.Second) fleet_name := aws.StringValue(CreateFleetInputOpts.Name) - get, err := svc.DescribeFleets(&appstream.DescribeFleetsInput { - Names: aws.StringSlice([]string{fleet_name}), + get, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{fleet_name}), }) + if err != nil { - log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) - return err + log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) + return err } + if get.Fleets == nil { - log.Printf("[DEBUG] Apsstream Fleet (%s) not found", d.Id()) + log.Printf("[DEBUG] Appstream Fleet (%s) not found", d.Id()) } - fleetArn := get.Fleets[0].Arn - tag, err := svc.TagResource(&appstream.TagResourceInput{ - ResourceArn: fleetArn, - Tags: tags, + ResourceArn: get.Fleets[0].Arn, + Tags: aws.StringMap(expandTags(v.(map[string]interface{}))), }) - if err != nil { - log.Printf("[ERROR] Error tagging Appstream Stack: %s", err) - return err - } - log.Printf("[DEBUG] %s", tag) - } - - if v, ok := d.GetOk("stack_name"); ok { - AssociateFleetInputOpts := &appstream.AssociateFleetInput{} - AssociateFleetInputOpts.FleetName = CreateFleetInputOpts.Name - AssociateFleetInputOpts.StackName = aws.String(v.(string)) - resp, err := svc.AssociateFleet(AssociateFleetInputOpts) if err != nil { - log.Printf("[ERROR] Error associating Appstream Fleet: %s", err) + log.Printf("[ERROR] Error tagging Appstream Fleet: %s", err) return err } - - log.Printf("[DEBUG] %s", resp) + log.Printf("[DEBUG] %s", tag) } if v, ok := d.GetOk("state"); ok { @@ -307,23 +296,23 @@ func resourceAppstreamFleetCreate(d *schema.ResourceData, meta interface{}) erro for { - resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*CreateFleetInputOpts.Name}), - }) + resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*CreateFleetInputOpts.Name}), + }) - if err != nil { - log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) - return err - } + if err != nil { + log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) + return err + } - curr_state := resp.Fleets[0].State - if aws.StringValue(curr_state) == desired_state{ - break - } - if aws.StringValue(curr_state) != desired_state { - time.Sleep(20 * time.Second) - continue - } + curr_state := resp.Fleets[0].State + if aws.StringValue(curr_state) == desired_state { + break + } + if aws.StringValue(curr_state) != desired_state { + time.Sleep(20 * time.Second) + continue + } } } @@ -361,13 +350,23 @@ func resourceAppstreamFleetRead(d *schema.ResourceData, meta interface{}) error d.Set("description", v.Description) d.Set("display_name", v.DisplayName) + d.Set("disconnect_timeout", v.DisconnectTimeoutInSeconds) d.Set("enable_default_internet_access", v.EnableDefaultInternetAccess) d.Set("fleet_type", v.FleetType) d.Set("image_arn", v.ImageArn) d.Set("iam_role_arn", v.IamRoleArn) + d.Set("idle_disconnect_timeout", v.IdleDisconnectTimeoutInSeconds) d.Set("instance_type", v.InstanceType) d.Set("max_user_duration", v.MaxUserDurationInSeconds) + d.Set("stream_view", v.StreamView) + + if v.DomainJoinInfo != nil { + dom_attr := map[string]interface{}{} + dom_attr["directory_name"] = v.DomainJoinInfo.DirectoryName + dom_attr["organizational_unit_distinguished_name"] = v.DomainJoinInfo.OrganizationalUnitDistinguishedName + d.Set("domain_info", dom_attr) + } if v.VpcConfig != nil { vpc_attr := map[string]interface{}{} @@ -377,13 +376,16 @@ func resourceAppstreamFleetRead(d *schema.ResourceData, meta interface{}) error vpc_attr["subnet_ids"] = aws.String(strings.Join(vpc_config_sub, ",")) d.Set("vpc_config", vpc_attr) } + tg, err := svc.ListTagsForResource(&appstream.ListTagsForResourceInput{ - ResourceArn: v.Arn, - }) - if err != nil { + ResourceArn: v.Arn, + }) + + if err != nil { log.Printf("[ERROR] Error listing stack tags: %s", err) return err - } + } + if tg.Tags == nil { log.Printf("[DEBUG] Apsstream Stack tags (%s) not found", d.Id()) return nil @@ -411,191 +413,236 @@ func resourceAppstreamFleetRead(d *schema.ResourceData, meta interface{}) error func resourceAppstreamFleetUpdate(d *schema.ResourceData, meta interface{}) error { svc := meta.(*AWSClient).appstreamconn - UpdateFleetInputOpts := &appstream.UpdateFleetInput{} - - d.Partial(true) - - if v, ok := d.GetOk("name"); ok { - UpdateFleetInputOpts.Name = aws.String(v.(string)) - } - - if d.HasChange("description") { - d.SetPartial("description") - log.Printf("[DEBUG] Modify Fleet") - description :=d.Get("description").(string) - UpdateFleetInputOpts.Description = aws.String(description) - } - - if d.HasChange("disconnect_timeout") { - d.SetPartial("disconnect_timeout") - log.Printf("[DEBUG] Modify Fleet") - disconnect_timeout := d.Get("disconnect_timeout").(int) - UpdateFleetInputOpts.DisconnectTimeoutInSeconds = aws.Int64(int64(disconnect_timeout)) - } - - if d.HasChange("display_name") { - d.SetPartial("display_name") - log.Printf("[DEBUG] Modify Fleet") - display_name :=d.Get("display_name").(string) - UpdateFleetInputOpts.DisplayName = aws.String(display_name) - } + UpdateFleetInputOpts := &appstream.UpdateFleetInput{} + + // d.Partial(true) + + if v, ok := d.GetOk("name"); ok { + UpdateFleetInputOpts.Name = aws.String(v.(string)) + } + + if d.HasChange("description") { + // d.SetPartial("description") + log.Printf("[DEBUG] Modify Fleet") + description := d.Get("description").(string) + UpdateFleetInputOpts.Description = aws.String(description) + } + + if d.HasChange("disconnect_timeout") { + // d.SetPartial("disconnect_timeout") + log.Printf("[DEBUG] Modify Fleet") + disconnect_timeout := d.Get("disconnect_timeout").(int) + UpdateFleetInputOpts.DisconnectTimeoutInSeconds = aws.Int64(int64(disconnect_timeout)) + } + + if d.HasChange("display_name") { + // d.SetPartial("display_name") + log.Printf("[DEBUG] Modify Fleet") + display_name := d.Get("display_name").(string) + UpdateFleetInputOpts.DisplayName = aws.String(display_name) + } + + if d.HasChange("enable_default_internet_access") { + log.Printf("[DEBUG] Modify Fleet") + enable_default_internet_access := d.Get("enable_default_internet_access").(bool) + UpdateFleetInputOpts.EnableDefaultInternetAccess = aws.Bool(enable_default_internet_access) + } + + if d.HasChange("idle_disconnect_timeout") { + log.Printf("[DEBUG] Modify Fleet") + idle_disconnect_timeout_in_seconds := d.Get("idle_disconnect_timeout").(int) + UpdateFleetInputOpts.IdleDisconnectTimeoutInSeconds = aws.Int64(int64(idle_disconnect_timeout_in_seconds)) + } if d.HasChange("image_arn") { - d.SetPartial("image_arn") - log.Printf("[DEBUG] Modify Fleet") - image_arn :=d.Get("image_arn").(string) - UpdateFleetInputOpts.ImageArn = aws.String(image_arn) + // d.SetPartial("image_arn") + log.Printf("[DEBUG] Modify Fleet") + image_arn := d.Get("image_arn").(string) + UpdateFleetInputOpts.ImageArn = aws.String(image_arn) + } + + if d.HasChange("image_name") { + log.Printf("[DEBUG] Modify Fleet") + image_name := d.Get("image_name").(string) + UpdateFleetInputOpts.ImageName = aws.String(image_name) } - + if d.HasChange("iam_role_arn") { - d.SetPartial("iam_role_arn") - log.Printf("[DEBUG] Modify Fleet") - iam_role_arn :=d.Get("iam_role_arn").(string) - UpdateFleetInputOpts.IamRoleArn = aws.String(iam_role_arn) - } - - if d.HasChange("instance_type") { - d.SetPartial("instance_type") - log.Printf("[DEBUG] Modify Fleet") - instance_type := d.Get("instance_type").(string) - UpdateFleetInputOpts.InstanceType = aws.String(instance_type) - } - - if d.HasChange("max_user_duration") { - d.SetPartial("max_user_duration") - log.Printf("[DEBUG] Modify Fleet") - max_user_duration :=d.Get("max_user_duration").(int) - UpdateFleetInputOpts.MaxUserDurationInSeconds = aws.Int64(int64(max_user_duration)) - } - - resp, err := svc.UpdateFleet(UpdateFleetInputOpts) - - if err != nil { - log.Printf("[ERROR] Error updating Appstream Fleet: %s", err) - return err - } - log.Printf("[DEBUG] %s", resp) - desired_state := d.Get("state") - if d.HasChange("state") { - d.SetPartial("state") - if desired_state == "STOPPED" { - svc.StopFleet(&appstream.StopFleetInput{ - Name: aws.String(d.Id()), - }) - for { - - resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*UpdateFleetInputOpts.Name}), - }) - if err != nil { + // d.SetPartial("iam_role_arn") + log.Printf("[DEBUG] Modify Fleet") + iam_role_arn := d.Get("iam_role_arn").(string) + UpdateFleetInputOpts.IamRoleArn = aws.String(iam_role_arn) + } + + if d.HasChange("instance_type") { + // d.SetPartial("instance_type") + log.Printf("[DEBUG] Modify Fleet") + instance_type := d.Get("instance_type").(string) + UpdateFleetInputOpts.InstanceType = aws.String(instance_type) + } + + if d.HasChange("max_user_duration") { + // d.SetPartial("max_user_duration") + log.Printf("[DEBUG] Modify Fleet") + max_user_duration := d.Get("max_user_duration").(int) + UpdateFleetInputOpts.MaxUserDurationInSeconds = aws.Int64(int64(max_user_duration)) + } + + if d.HasChange("stream_view") { + // d.SetPartial("stream_view") + log.Printf("[DEBUG] Modify Fleet") + stream_view := d.Get("stream_view").(string) + UpdateFleetInputOpts.StreamView = aws.String(stream_view) + } + + resp, err := svc.UpdateFleet(UpdateFleetInputOpts) + + if err != nil { + log.Printf("[ERROR] Error updating Appstream Fleet: %s", err) + return err + } + + log.Printf("[DEBUG] Appstream Fleet updated %s ", resp) + + if v, ok := d.GetOk("tags"); ok && d.HasChange("tags") { + time.Sleep(2 * time.Second) + + fleet_name := aws.StringValue(UpdateFleetInputOpts.Name) + get, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{fleet_name}), + }) + + if err != nil { log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) return err - } - - curr_state := resp.Fleets[0].State - if aws.StringValue(curr_state) == desired_state{ - break - } - if aws.StringValue(curr_state) != desired_state { - time.Sleep(20 * time.Second) - continue - } - - } - } else if desired_state == "RUNNING" { - svc.StartFleet(&appstream.StartFleetInput{ - Name: aws.String(d.Id()), - }) - for { - - resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*UpdateFleetInputOpts.Name}), - }) - if err != nil { - log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) + } + + if get.Fleets == nil { + log.Printf("[DEBUG] Appstream Fleet (%s) not found", d.Id()) + } + + tag, err := svc.TagResource(&appstream.TagResourceInput{ + ResourceArn: get.Fleets[0].Arn, + Tags: aws.StringMap(expandTags(v.(map[string]interface{}))), + }) + + if err != nil { + log.Printf("[ERROR] Error tagging Appstream Fleet: %s", err) return err - } - - curr_state := resp.Fleets[0].State - if aws.StringValue(curr_state) == desired_state{ - break - } - if aws.StringValue(curr_state) != desired_state { - time.Sleep(20 * time.Second) - continue - } - - } - } - } - d.Partial(false) - return resourceAppstreamFleetRead(d, meta) + } + + log.Printf("[DEBUG] %s", tag) + } + + desired_state := d.Get("state") + if d.HasChange("state") { + + if desired_state == "STOPPED" { + svc.StopFleet(&appstream.StopFleetInput{ + Name: aws.String(d.Id()), + }) + + for { + resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*UpdateFleetInputOpts.Name}), + }) + if err != nil { + log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) + return err + } + + curr_state := resp.Fleets[0].State + if aws.StringValue(curr_state) == desired_state { + break + } + + if aws.StringValue(curr_state) != desired_state { + time.Sleep(20 * time.Second) + continue + } + } + } else if desired_state == "RUNNING" { + svc.StartFleet(&appstream.StartFleetInput{ + Name: aws.String(d.Id()), + }) + + for { + resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*UpdateFleetInputOpts.Name}), + }) + + if err != nil { + log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) + return err + } + + curr_state := resp.Fleets[0].State + if aws.StringValue(curr_state) == desired_state { + break + } + if aws.StringValue(curr_state) != desired_state { + time.Sleep(20 * time.Second) + continue + } + + } + } + } + + return resourceAppstreamFleetRead(d, meta) } func resourceAppstreamFleetDelete(d *schema.ResourceData, meta interface{}) error { svc := meta.(*AWSClient).appstreamconn - resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*aws.String(d.Id())}), - }) + resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*aws.String(d.Id())}), + }) + + if err != nil { + log.Printf("[ERROR] Error reading Appstream Fleet: %s", err) + return err + } + + curr_state := aws.StringValue(resp.Fleets[0].State) - if err != nil { - log.Printf("[ERROR] Error reading Appstream Fleet: %s", err) - return err - } + if curr_state == "RUNNING" { + desired_state := "STOPPED" + svc.StopFleet(&appstream.StopFleetInput{ + Name: aws.String(d.Id()), + }) + for { - curr_state := aws.StringValue(resp.Fleets[0].State) + resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ + Names: aws.StringSlice([]string{*aws.String(d.Id())}), + }) + if err != nil { + log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) + return err + } - if curr_state == "RUNNING" { - desired_state := "STOPPED" - svc.StopFleet(&appstream.StopFleetInput{ - Name: aws.String(d.Id()), - }) - for { + curr_state := resp.Fleets[0].State + if aws.StringValue(curr_state) == desired_state { + break + } + if aws.StringValue(curr_state) != desired_state { + time.Sleep(20 * time.Second) + continue + } + } + } - resp, err := svc.DescribeFleets(&appstream.DescribeFleetsInput{ - Names: aws.StringSlice([]string{*aws.String(d.Id())}), - }) - if err != nil { - log.Printf("[ERROR] Error describing Appstream Fleet: %s", err) - return err - } - - curr_state := resp.Fleets[0].State - if aws.StringValue(curr_state) == desired_state{ - break - } - if aws.StringValue(curr_state) != desired_state { - time.Sleep(20 * time.Second) - continue - } - - } - - } - - - - dis, err := svc.DisassociateFleet(&appstream.DisassociateFleetInput{ - FleetName: aws.String(d.Id()), - StackName: aws.String(d.Get("stack_name").(string)), - }) - if err != nil { - log.Printf("[ERROR] Error deleting Appstream Fleet: %s", err) - return err - } - log.Printf("[DEBUG] %s", dis) - - del, err := svc.DeleteFleet(&appstream.DeleteFleetInput{ - Name: aws.String(d.Id()), - }) - if err != nil { - log.Printf("[ERROR] Error deleting Appstream Fleet: %s", err) - return err - } - log.Printf("[DEBUG] %s", del) - return nil + del, err := svc.DeleteFleet(&appstream.DeleteFleetInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + log.Printf("[ERROR] Error deleting Appstream Fleet: %s", err) + return err + } + + log.Printf("[DEBUG] %s", del) + return nil } diff --git a/appstream/resource_image_builder.go b/appstream/resource_image_builder.go index ee89d6a..59b1110 100644 --- a/appstream/resource_image_builder.go +++ b/appstream/resource_image_builder.go @@ -1,210 +1,241 @@ package appstream import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/appstream" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "log" - "strings" - "time" -) + "log" + "strings" + "time" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) func resourceAppstreamImageBuilder() *schema.Resource { - return &schema.Resource { - Create: resourceAppstreamImageBuilderCreate, - Read: resourceAppstreamImageBuilderRead, - Update: resourceAppstreamImageBuilderUpdate, - Delete: resourceAppstreamImageBuilderDelete, - Importer: &schema.ResourceImporter { - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - "appstream_agent_version": { - Type: schema.TypeString, - Optional: true, - }, - "description": { - Type: schema.TypeString, - Optional: true, - }, - - "display_name": { - Type: schema.TypeString, - Optional: true, - }, - - "domain_info": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "directory_name": { - Type: schema.TypeString, - Optional: true, - }, - "organizational_unit_distinguished_name": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - - "enable_default_internet_access": { - Type: schema.TypeBool, - Optional: true, - }, - - "image_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "instance_type": { - Type: schema.TypeString, - Required: true, - }, - - "state" : { - Type: schema.TypeString, - Optional: true, - }, - - "vpc_config": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource { - Schema: map[string]*schema.Schema { - "security_group_ids": { - Type: schema.TypeString, - Optional: true, - }, - "subnet_ids": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, - } + return &schema.Resource{ + Create: resourceAppstreamImageBuilderCreate, + Read: resourceAppstreamImageBuilderRead, + Update: resourceAppstreamImageBuilderUpdate, + Delete: resourceAppstreamImageBuilderDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "access_endpoints": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "endpoint_type": { + Type: schema.TypeString, + Required: true, + }, + "vpce_id": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "appstream_agent_version": { + Type: schema.TypeString, + Optional: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + + "display_name": { + Type: schema.TypeString, + Optional: true, + }, + + "domain_info": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "directory_name": { + Type: schema.TypeString, + Optional: true, + }, + "organizational_unit_distinguished_name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + + "enable_default_internet_access": { + Type: schema.TypeBool, + Optional: true, + }, + + "image_arn": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "image_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "instance_type": { + Type: schema.TypeString, + Required: true, + }, + + "state": { + Type: schema.TypeString, + Optional: true, + }, + + "vpc_config": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_group_ids": { + Type: schema.TypeString, + Optional: true, + }, + "subnet_ids": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + }, + } } func resourceAppstreamImageBuilderCreate(d *schema.ResourceData, meta interface{}) error { svc := meta.(*AWSClient).appstreamconn - CreateImageBuilderInputOpts := &appstream.CreateImageBuilderInput{} + CreateImageBuilderInputOpts := &appstream.CreateImageBuilderInput{} - if v, ok := d.GetOk("name"); ok { - CreateImageBuilderInputOpts.Name = aws.String(v.(string)) - } + if v, ok := d.GetOk("name"); ok { + CreateImageBuilderInputOpts.Name = aws.String(v.(string)) + } - if v, ok := d.GetOk("appstream_agent_version"); ok { - CreateImageBuilderInputOpts.AppstreamAgentVersion = aws.String(v.(string)) - } + if v, ok := d.GetOk("access_endpoints"); ok { + accessEndpointConfigs := v.(*schema.Set).List() + CreateImageBuilderInputOpts.AccessEndpoints = expandAccessEndpointConfigs(accessEndpointConfigs) + } - if v, ok := d.GetOk("description"); ok { - CreateImageBuilderInputOpts.Description = aws.String(v.(string)) - } + if v, ok := d.GetOk("appstream_agent_version"); ok { + CreateImageBuilderInputOpts.AppstreamAgentVersion = aws.String(v.(string)) + } - if v, ok := d.GetOk("display_name"); ok { - CreateImageBuilderInputOpts.DisplayName = aws.String(v.(string)) - } + if v, ok := d.GetOk("description"); ok { + CreateImageBuilderInputOpts.Description = aws.String(v.(string)) + } + + if v, ok := d.GetOk("display_name"); ok { + CreateImageBuilderInputOpts.DisplayName = aws.String(v.(string)) + } - DomainJoinInfoConfig := &appstream.DomainJoinInfo{} + DomainJoinInfoConfig := &appstream.DomainJoinInfo{} - if dom, ok := d.GetOk("domain_info"); ok { - DomainAttributes := dom.([]interface{}) - attr := DomainAttributes[0].(map[string]interface{}) - if v, ok := attr["directory_name"]; ok { - DomainJoinInfoConfig.DirectoryName = aws.String(v.(string)) + if dom, ok := d.GetOk("domain_info"); ok { + DomainAttributes := dom.([]interface{}) + attr := DomainAttributes[0].(map[string]interface{}) + if v, ok := attr["directory_name"]; ok { + DomainJoinInfoConfig.DirectoryName = aws.String(v.(string)) + } + if v, ok := attr["organizational_unit_distinguished_name"]; ok { + DomainJoinInfoConfig.OrganizationalUnitDistinguishedName = aws.String(v.(string)) + } + CreateImageBuilderInputOpts.DomainJoinInfo = DomainJoinInfoConfig } - if v, ok := attr["organizational_unit_distinguished_name"]; ok { - DomainJoinInfoConfig.OrganizationalUnitDistinguishedName = aws.String(v.(string)) + + if v, ok := d.GetOk("enable_default_internet_access"); ok { + CreateImageBuilderInputOpts.EnableDefaultInternetAccess = aws.Bool(v.(bool)) + } + + if v, ok := d.GetOk("image_arn"); ok { + CreateImageBuilderInputOpts.ImageArn = aws.String(v.(string)) + } + + if v, ok := d.GetOk("image_name"); ok { + CreateImageBuilderInputOpts.ImageName = aws.String(v.(string)) } - CreateImageBuilderInputOpts.DomainJoinInfo = DomainJoinInfoConfig - } - - if v, ok := d.GetOk("enable_default_internet_access"); ok { - CreateImageBuilderInputOpts.EnableDefaultInternetAccess = aws.Bool(v.(bool)) - } - - if v, ok := d.GetOk("image_arn"); ok { - CreateImageBuilderInputOpts.ImageArn = aws.String(v.(string)) - } - - if v, ok := d.GetOk("instance_type"); ok { - CreateImageBuilderInputOpts.InstanceType = aws.String(v.(string)) - } - - VpcConfigConfig := & appstream.VpcConfig{} - - if vpc, ok := d.GetOk("vpc_config"); ok { - VpcAttributes := vpc.([]interface{}) - attr := VpcAttributes[0].(map[string]interface{}) - if v, ok := attr["security_group_ids"]; ok { - strSlice := strings.Split(v.(string), ",") - for i, s := range strSlice { - strSlice[i] = strings.TrimSpace(s) + + if v, ok := d.GetOk("instance_type"); ok { + CreateImageBuilderInputOpts.InstanceType = aws.String(v.(string)) + } + + VpcConfigConfig := &appstream.VpcConfig{} + + if vpc, ok := d.GetOk("vpc_config"); ok { + VpcAttributes := vpc.([]interface{}) + attr := VpcAttributes[0].(map[string]interface{}) + if v, ok := attr["security_group_ids"]; ok { + strSlice := strings.Split(v.(string), ",") + for i, s := range strSlice { + strSlice[i] = strings.TrimSpace(s) + } + VpcConfigConfig.SecurityGroupIds = aws.StringSlice(strSlice) } - VpcConfigConfig.SecurityGroupIds = aws.StringSlice(strSlice) - } - if v, ok := attr["subnet_ids"]; ok { - strSlice := strings.Split(v.(string), ",") - for i, s := range strSlice { - strSlice[i] = strings.TrimSpace(s) + if v, ok := attr["subnet_ids"]; ok { + strSlice := strings.Split(v.(string), ",") + for i, s := range strSlice { + strSlice[i] = strings.TrimSpace(s) + } + VpcConfigConfig.SubnetIds = aws.StringSlice(strSlice) } - VpcConfigConfig.SubnetIds = aws.StringSlice(strSlice) - } - CreateImageBuilderInputOpts.VpcConfig = VpcConfigConfig - } + CreateImageBuilderInputOpts.VpcConfig = VpcConfigConfig + } - log.Printf("[DEBUG] Run configuration: %s", CreateImageBuilderInputOpts) + log.Printf("[DEBUG] Run configuration: %s", CreateImageBuilderInputOpts) - resp, err := svc.CreateImageBuilder(CreateImageBuilderInputOpts) + resp, err := svc.CreateImageBuilder(CreateImageBuilderInputOpts) - if err != nil { - log.Printf("[ERROR] Error creating Appstream Image Builder: %s", err) - return err - } + if err != nil { + log.Printf("[ERROR] Error creating Appstream Image Builder: %s", err) + return err + } - log.Printf("[DEBUG] Image builder created %s", resp) + log.Printf("[DEBUG] Image builder created %s", resp) - ImageBuilderName := aws.StringValue(CreateImageBuilderInputOpts.Name) - for { + ImageBuilderName := aws.StringValue(CreateImageBuilderInputOpts.Name) + for { - resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ - Names: aws.StringSlice([]string{ImageBuilderName}), - }) + resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ + Names: aws.StringSlice([]string{ImageBuilderName}), + }) - if err != nil { - log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) - return err - } + if err != nil { + log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) + return err + } - state := resp.ImageBuilders[0].State - if aws.StringValue(state) == "RUNNING" { - break - } - if aws.StringValue(state) != "RUNNING" { - log.Printf("[DEBUG] Image Builder not running") - time.Sleep(20 * time.Second) - continue - } + state := resp.ImageBuilders[0].State + if aws.StringValue(state) == "RUNNING" { + break + } + if aws.StringValue(state) != "RUNNING" { + log.Printf("[DEBUG] Image Builder not running") + time.Sleep(20 * time.Second) + continue + } - } + } - d.SetId(*CreateImageBuilderInputOpts.Name) + d.SetId(*CreateImageBuilderInputOpts.Name) - return resourceAppstreamImageBuilderRead(d, meta) + return resourceAppstreamImageBuilderRead(d, meta) } func resourceAppstreamImageBuilderRead(d *schema.ResourceData, meta interface{}) error { @@ -216,38 +247,55 @@ func resourceAppstreamImageBuilderRead(d *schema.ResourceData, meta interface{}) names = append(names, &imageBuilderName) describeImageBuildersInput := appstream.DescribeImageBuildersInput{Names: names} - resp, err := svc.DescribeImageBuilders(&describeImageBuildersInput) - if err != nil { - log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) - return err - } - - - for _, v := range resp.ImageBuilders { - - if aws.StringValue(v.Name) == d.Get("name") { - d.Set("name", v.Name) - d.Set("description", v.Description) - d.Set("display_name", v.DisplayName) - d.Set("appstream_agent_version", v.AppstreamAgentVersion) - d.Set("enable_default_internet_access", v.EnableDefaultInternetAccess) - d.Set("instance_type", v.InstanceType) - d.Set("image_arn", d.Get("image_arn")) - d.Set("state", v.State) - if v.VpcConfig != nil { - vpc_attr := map[string]interface{}{} - vpc_config_sg := aws.StringValueSlice(v.VpcConfig.SecurityGroupIds) - vpc_config_sub := aws.StringValueSlice(v.VpcConfig.SubnetIds) - vpc_attr["security_group_ids"] = aws.String(strings.Join(vpc_config_sg, ",")) - vpc_attr["subnet_ids"] = aws.String(strings.Join(vpc_config_sub, ",")) - d.Set("vpc_config", vpc_attr) - } - return nil - } - } - - d.SetId("") - return nil + resp, err := svc.DescribeImageBuilders(&describeImageBuildersInput) + + if err != nil { + log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) + return err + } + + for _, v := range resp.ImageBuilders { + + if aws.StringValue(v.Name) == d.Get("name") { + d.Set("name", v.Name) + d.Set("description", v.Description) + d.Set("display_name", v.DisplayName) + d.Set("appstream_agent_version", v.AppstreamAgentVersion) + d.Set("enable_default_internet_access", v.EnableDefaultInternetAccess) + d.Set("instance_type", v.InstanceType) + d.Set("image_arn", d.Get("image_arn")) + d.Set("image_arn", d.Get("image_arn")) + d.Set("image_name", d.Get("image_name")) + d.Set("state", v.State) + if v.VpcConfig != nil { + vpc_attr := map[string]interface{}{} + vpc_config_sg := aws.StringValueSlice(v.VpcConfig.SecurityGroupIds) + vpc_config_sub := aws.StringValueSlice(v.VpcConfig.SubnetIds) + vpc_attr["security_group_ids"] = aws.String(strings.Join(vpc_config_sg, ",")) + vpc_attr["subnet_ids"] = aws.String(strings.Join(vpc_config_sub, ",")) + d.Set("vpc_config", vpc_attr) + } + aeAttr := map[string]interface{}{} + aeRes := make([]map[string]interface{}, 0) + + ae := v.AccessEndpoints + if len(ae) > 0 && len(ae) < 5 { + aeAttr["endpoint_type"] = aws.StringValue(ae[0].EndpointType) + aeAttr["vpce_id"] = aws.StringValue(ae[0].VpceId) + aeRes = append(aeRes, aeAttr) + } + + if len(aeRes) > 0 { + if err := d.Set("access_endpoints", aeRes); err != nil { + log.Printf("[ERROR] Error setting access endpoints: %s", err) + } + } + return nil + } + } + + d.SetId("") + return nil } @@ -256,115 +304,114 @@ func resourceAppstreamImageBuilderUpdate(d *schema.ResourceData, meta interface{ svc := meta.(*AWSClient).appstreamconn - StartImageBuilderInputOptions := &appstream.StartImageBuilderInput{} - StopImageBuilderInputOptions := &appstream.StopImageBuilderInput{} + StartImageBuilderInputOptions := &appstream.StartImageBuilderInput{} + StopImageBuilderInputOptions := &appstream.StopImageBuilderInput{} - d.Partial(true) + d.Partial(true) - if v, ok := d.GetOk("name"); ok { - StartImageBuilderInputOptions.Name = aws.String(v.(string)) - StopImageBuilderInputOptions.Name = aws.String(v.(string)) - } + if v, ok := d.GetOk("name"); ok { + StartImageBuilderInputOptions.Name = aws.String(v.(string)) + StopImageBuilderInputOptions.Name = aws.String(v.(string)) + } - desired_state := d.Get("state") + desired_state := d.Get("state") - if d.HasChange("state") { - d.SetPartial("state") - if desired_state == "STOPPED" { - svc.StopImageBuilder(StopImageBuilderInputOptions) - } else if desired_state == "RUNNING" { - svc.StartImageBuilder(StartImageBuilderInputOptions) - } + if d.HasChange("state") { + // d.SetPartial("state") + if desired_state == "STOPPED" { + svc.StopImageBuilder(StopImageBuilderInputOptions) + } else if desired_state == "RUNNING" { + svc.StartImageBuilder(StartImageBuilderInputOptions) + } - for { + for { - resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ - Names: aws.StringSlice([]string{*StartImageBuilderInputOptions.Name}), - }) + resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ + Names: aws.StringSlice([]string{*StartImageBuilderInputOptions.Name}), + }) - if err != nil { - log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) - return err - } + if err != nil { + log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) + return err + } - curr_state := resp.ImageBuilders[0].State - if aws.StringValue(curr_state) == desired_state{ - break - } - if aws.StringValue(curr_state) != desired_state { - time.Sleep(20 * time.Second) - continue - } + curr_state := resp.ImageBuilders[0].State + if aws.StringValue(curr_state) == desired_state { + break + } + if aws.StringValue(curr_state) != desired_state { + time.Sleep(20 * time.Second) + continue + } - } - } + } + } - d.Partial(false) - return resourceAppstreamImageBuilderRead(d, meta) + d.Partial(false) + return resourceAppstreamImageBuilderRead(d, meta) } func resourceAppstreamImageBuilderDelete(d *schema.ResourceData, meta interface{}) error { svc := meta.(*AWSClient).appstreamconn - ImageBuilderName := d.Id() - - resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ - Names: aws.StringSlice([]string{ImageBuilderName}), - }) + ImageBuilderName := d.Id() - if err != nil { - log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) - return err - } + resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ + Names: aws.StringSlice([]string{ImageBuilderName}), + }) - state := resp.ImageBuilders[0].State + if err != nil { + log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) + return err + } - if aws.StringValue(state) == "RUNNING" { - resp, err := svc.StopImageBuilder(&appstream.StopImageBuilderInput{ - Name: aws.String(d.Id()), - }) + state := resp.ImageBuilders[0].State - if err != nil { - log.Printf("[ERROR] Error stopping Appstream Image Builder: %s", err) - return err - } + if aws.StringValue(state) == "RUNNING" { + resp, err := svc.StopImageBuilder(&appstream.StopImageBuilderInput{ + Name: aws.String(d.Id()), + }) - log.Printf("[DEBUG] %s", resp) + if err != nil { + log.Printf("[ERROR] Error stopping Appstream Image Builder: %s", err) + return err + } - for { + log.Printf("[DEBUG] %s", resp) - resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ - Names: aws.StringSlice([]string{ImageBuilderName}), - }) + for { - if err != nil { - log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) - return err - } + resp, err := svc.DescribeImageBuilders(&appstream.DescribeImageBuildersInput{ + Names: aws.StringSlice([]string{ImageBuilderName}), + }) - state := resp.ImageBuilders[0].State - if aws.StringValue(state) == "STOPPED" { - break - } - if aws.StringValue(state) != "STOPPED" { - log.Printf("[DEBUG] Image Builder not running") - time.Sleep(20 * time.Second) - continue - } + if err != nil { + log.Printf("[ERROR] Error describing Appstream Image Builder: %s", err) + return err + } - } - } + state := resp.ImageBuilders[0].State + if aws.StringValue(state) == "STOPPED" { + break + } + if aws.StringValue(state) != "STOPPED" { + log.Printf("[DEBUG] Image Builder not running") + time.Sleep(20 * time.Second) + continue + } + } + } - del, err := svc.DeleteImageBuilder(&appstream.DeleteImageBuilderInput{ - Name: aws.String(d.Id()), - }) - if err != nil { - log.Printf("[ERROR] Error deleting Appstream Image Builder: %s", err) - return err - } - log.Printf("[DEBUG] %s", del) + del, err := svc.DeleteImageBuilder(&appstream.DeleteImageBuilderInput{ + Name: aws.String(d.Id()), + }) + if err != nil { + log.Printf("[ERROR] Error deleting Appstream Image Builder: %s", err) + return err + } + log.Printf("[DEBUG] %s", del) - return nil + return nil } diff --git a/appstream/resource_stack.go b/appstream/resource_stack.go index 2fe4a61..f800eb5 100644 --- a/appstream/resource_stack.go +++ b/appstream/resource_stack.go @@ -1,11 +1,12 @@ package appstream import ( + "log" + "time" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appstream" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "log" - "time" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) func resourceAppstreamStack() *schema.Resource { @@ -19,6 +20,40 @@ func resourceAppstreamStack() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "access_endpoints": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "endpoint_type": { + Type: schema.TypeString, + Required: true, + }, + "vpce_id": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "application_settings": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "settings_group": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "description": { Type: schema.TypeString, Optional: true, @@ -29,6 +64,14 @@ func resourceAppstreamStack() *schema.Resource { Optional: true, }, + "embed_host_domains": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "feedback_url": { Type: schema.TypeString, Optional: true, @@ -47,43 +90,48 @@ func resourceAppstreamStack() *schema.Resource { "storage_connectors": { Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "connector_type": { Type: schema.TypeString, Required: true, }, + "domains": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Computed: true, + }, + "resource_identifier": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, }, }, }, + "tags": { Type: schema.TypeMap, Optional: true, }, + "user_settings": { - Type: schema.TypeSet, + Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "file_download":{ - Type: schema.TypeString, - Optional: true, - }, - "file_upload":{ - Type: schema.TypeString, - Optional: true, - }, - "copy_from_local": { - Type: schema.TypeString, - Optional: true, - }, - "copy_to_local": { - Type: schema.TypeString, - Optional: true, + "action": { + Type: schema.TypeString, + Required: true, }, - "allow_local_device_printing": { - Type: schema.TypeString, - Optional: true, + "permission": { + Type: schema.TypeString, + Required: true, }, }, }, @@ -97,6 +145,13 @@ func resourceAppstreamStackCreate(d *schema.ResourceData, meta interface{}) erro svc := meta.(*AWSClient).appstreamconn CreateStackInputOpts := &appstream.CreateStackInput{} + if v, ok := d.GetOk("access_endpoints"); ok { + CreateStackInputOpts.AccessEndpoints = expandAccessEndpointsConfigs(v.(*schema.Set).List()) + } + + if v, ok := d.GetOk("application_settings"); ok { + CreateStackInputOpts.ApplicationSettings = expandApplicationSettings(v.([]interface{})) + } if v, ok := d.GetOk("name"); ok { CreateStackInputOpts.Name = aws.String(v.(string)) @@ -110,6 +165,10 @@ func resourceAppstreamStackCreate(d *schema.ResourceData, meta interface{}) erro CreateStackInputOpts.DisplayName = aws.String(v.(string)) } + if v, ok := d.GetOk("embed_host_domains"); ok { + CreateStackInputOpts.EmbedHostDomains = expandStringSet(v.(*schema.Set)) + } + if v, ok := d.GetOk("feedback_url"); ok { CreateStackInputOpts.FeedbackURL = aws.String(v.(string)) } @@ -119,13 +178,11 @@ func resourceAppstreamStackCreate(d *schema.ResourceData, meta interface{}) erro } if v, ok := d.GetOk("storage_connectors"); ok { - storageConnectorConfigs := v.(*schema.Set).List() - CreateStackInputOpts.StorageConnectors = expandStorageConnectorConfigs(storageConnectorConfigs) + CreateStackInputOpts.StorageConnectors = expandStorageConnectorConfigs(v.(*schema.Set).List()) } if v, ok := d.GetOk("user_settings"); ok { - userSettingConfigs := v.(*schema.Set).List() - CreateStackInputOpts.UserSettings = expandUserSettingConfigs(userSettingConfigs) + CreateStackInputOpts.UserSettings = expandUserSettingConfigs(v.(*schema.Set).List()) } log.Printf("[DEBUG] Run configuration: %s", CreateStackInputOpts) @@ -133,47 +190,42 @@ func resourceAppstreamStackCreate(d *schema.ResourceData, meta interface{}) erro resp, err := svc.CreateStack(CreateStackInputOpts) if err != nil { - log.Printf("[ERROR] Error creating Appstream Cluster: %s", err) + log.Printf("[ERROR] Error creating Appstream stack: %s", err) return err } - log.Printf("[DEBUG] Appstream stack created %s ", resp) - time.Sleep(2 * time.Second) - if v, ok := d.GetOk("tags"); ok { - data_tags := v.(map[string]interface{}) - attr := make(map[string]string) - for k, v := range data_tags { - attr[k] = v.(string) - } + log.Printf("[DEBUG] Appstream stack created %s ", resp) - tags := aws.StringMap(attr) + if v, ok := d.GetOk("tags"); ok { + time.Sleep(2 * time.Second) stack_name := aws.StringValue(CreateStackInputOpts.Name) get, err := svc.DescribeStacks(&appstream.DescribeStacksInput{ Names: aws.StringSlice([]string{stack_name}), }) + if err != nil { - log.Printf("[ERROR] Error describing Appstream Stack: %s", err) + log.Printf("[ERROR] Error describing Appstream stack: %s", err) return err } + if get.Stacks == nil { - log.Printf("[DEBUG] Apsstream Stack (%s) not found", d.Id()) + log.Printf("[DEBUG] Appstream stack (%s) not found", d.Id()) } - stackArn := get.Stacks[0].Arn - tag, err := svc.TagResource(&appstream.TagResourceInput{ - ResourceArn: stackArn, - Tags: tags, + ResourceArn: get.Stacks[0].Arn, + Tags: aws.StringMap(expandTags(v.(map[string]interface{}))), }) + if err != nil { - log.Printf("[ERROR] Error tagging Appstream Stack: %s", err) + log.Printf("[ERROR] Error tagging Appstream stack: %s", err) return err } + log.Printf("[DEBUG] %s", tag) } - log.Printf("[DEBUG] %s", resp) d.SetId(*CreateStackInputOpts.Name) return resourceAppstreamStackRead(d, meta) @@ -200,20 +252,35 @@ func resourceAppstreamStackRead(d *schema.ResourceData, meta interface{}) error d.Set("name", v.Name) d.Set("description", v.Description) d.Set("display_name", v.DisplayName) + d.Set("embed_host_domains", v.EmbedHostDomains) d.Set("feedback_url", v.FeedbackURL) d.Set("redirect_url", v.RedirectURL) - attr := map[string]interface{}{} - res := make([]map[string]interface{}, 0) + ae_res := make([]map[string]interface{}, 0) + for _, raw := range v.AccessEndpoints { + ae_attr := map[string]interface{}{} + ae_attr["endpoint_type"] = aws.StringValue(raw.EndpointType) + ae_attr["vpce_id"] = aws.StringValue(raw.VpceId) + ae_res = append(ae_res, ae_attr) + } - sc := v.StorageConnectors - if len(sc) > 0 { - attr["connector_type"] = aws.StringValue(sc[0].ConnectorType) - res = append(res, attr) + if len(ae_res) > 0 { + if err := d.Set("access_endpoints", ae_res); err != nil { + log.Printf("[ERROR] Error setting access endpoints: %s", err) + } } - if len(res) > 0 { - if err := d.Set("storage_connectors", res); err != nil { + sc_res := make([]map[string]interface{}, 0) + for _, raw := range v.StorageConnectors { + sc_attr := map[string]interface{}{} + sc_attr["connector_type"] = aws.StringValue(raw.ConnectorType) + sc_attr["domains"] = aws.StringValueSlice(raw.Domains) + sc_attr["resource_identifier"] = aws.StringValue(raw.ResourceIdentifier) + sc_res = append(sc_res, sc_attr) + } + + if len(sc_res) > 0 { + if err := d.Set("storage_connectors", sc_res); err != nil { log.Printf("[ERROR] Error setting storage connector: %s", err) } } @@ -221,12 +288,14 @@ func resourceAppstreamStackRead(d *schema.ResourceData, meta interface{}) error tg, err := svc.ListTagsForResource(&appstream.ListTagsForResourceInput{ ResourceArn: v.Arn, }) + if err != nil { log.Printf("[ERROR] Error listing stack tags: %s", err) return err } + if tg.Tags == nil { - log.Printf("[DEBUG] Apsstream Stack tags (%s) not found", d.Id()) + log.Printf("[DEBUG] Appstream stack tags (%s) not found", d.Id()) return nil } @@ -238,13 +307,26 @@ func resourceAppstreamStackRead(d *schema.ResourceData, meta interface{}) error } d.Set("tags", tags_attr) } + + us_res := make([]map[string]interface{}, 0) + for _, raw := range v.UserSettings { + us_attr := map[string]interface{}{} + us_attr["action"] = aws.StringValue(raw.Action) + us_attr["permission"] = aws.StringValue(raw.Permission) + us_res = append(us_res, us_attr) + } + if len(us_res) > 0 { + if err := d.Set("user_settings", us_res); err != nil { + log.Printf("[ERROR] Error setting user settings: %s", err) + } + } return nil } } d.SetId("") - return nil + return nil } func resourceAppstreamStackUpdate(d *schema.ResourceData, meta interface{}) error { @@ -253,61 +335,110 @@ func resourceAppstreamStackUpdate(d *schema.ResourceData, meta interface{}) erro UpdateStackInputOpts := &appstream.UpdateStackInput{} - d.Partial(true) - if v, ok := d.GetOk("name"); ok { UpdateStackInputOpts.Name = aws.String(v.(string)) } + if d.HasChange("access_endpoints") { + log.Printf("[DEBUG] Modify appstream stack") + access_endpoints := d.Get("access_endpoints").(*schema.Set).List() + UpdateStackInputOpts.AccessEndpoints = expandAccessEndpointsConfigs(access_endpoints) + } + + if d.HasChange("application_settings") { + log.Printf("[DEBUG] Modify appstream stack") + application_settings := d.Get("application_settings").([]interface{}) + UpdateStackInputOpts.ApplicationSettings = expandApplicationSettings(application_settings) + } + if d.HasChange("description") { - d.SetPartial("description") log.Printf("[DEBUG] Modify appstream stack") description := d.Get("description").(string) UpdateStackInputOpts.Description = aws.String(description) } if d.HasChange("display_name") { - d.SetPartial("display_name") log.Printf("[DEBUG] Modify appstream stack") displayname := d.Get("display_name").(string) UpdateStackInputOpts.DisplayName = aws.String(displayname) } + if d.HasChange("embed_host_domains") { + log.Printf("[DEBUG] Modify appstream stack") + embed_host_domains := d.Get("embed_host_domains").(*schema.Set) + UpdateStackInputOpts.EmbedHostDomains = expandStringSet(embed_host_domains) + } + if d.HasChange("feedback_url") { - d.SetPartial("feedback_url") log.Printf("[DEBUG] Modify appstream stack") feedbackurl := d.Get("feedback_url").(string) UpdateStackInputOpts.FeedbackURL = aws.String(feedbackurl) } if d.HasChange("redirect_url") { - d.SetPartial("redirect_url") log.Printf("[DEBUG] Modify appstream stack") redirecturl := d.Get("redirect_url").(string) UpdateStackInputOpts.RedirectURL = aws.String(redirecturl) } + if d.HasChange("storage_connectors") { + log.Printf("[DEBUG] Modify appstream stack") + storage_connectors := d.Get("storage_connectors").(*schema.Set).List() + UpdateStackInputOpts.StorageConnectors = expandStorageConnectorConfigs(storage_connectors) + } + + if d.HasChange("user_settings") { + log.Printf("[DEBUG] Modify appstream stack") + user_settings := d.Get("user_settings").(*schema.Set).List() + UpdateStackInputOpts.UserSettings = expandUserSettingConfigs(user_settings) + } + resp, err := svc.UpdateStack(UpdateStackInputOpts) if err != nil { log.Printf("[ERROR] Error updating Appstream Stack: %s", err) return err } - log.Printf("[DEBUG] %s", resp) - d.Partial(false) - return nil + log.Printf("[DEBUG] Appstream Stack updated %s ", resp) + if v, ok := d.GetOk("tags"); ok && d.HasChange("tags") { + time.Sleep(2 * time.Second) + + stack_name := aws.StringValue(UpdateStackInputOpts.Name) + get, err := svc.DescribeStacks(&appstream.DescribeStacksInput{ + Names: aws.StringSlice([]string{stack_name}), + }) + + if err != nil { + log.Printf("[ERROR] Error describing Appstream Stack: %s", err) + return err + } + + if get.Stacks == nil { + log.Printf("[DEBUG] Appstream Stack (%s) not found", d.Id()) + } + tag, err := svc.TagResource(&appstream.TagResourceInput{ + ResourceArn: get.Stacks[0].Arn, + Tags: aws.StringMap(expandTags(v.(map[string]interface{}))), + }) + if err != nil { + log.Printf("[ERROR] Error tagging Appstream Stack: %s", err) + return err + } + log.Printf("[DEBUG] %s", tag) + } + return nil } func resourceAppstreamStackDelete(d *schema.ResourceData, meta interface{}) error { - svc := meta.(*AWSClient).appstreamconn resp, err := svc.DeleteStack(&appstream.DeleteStackInput{ Name: aws.String(d.Id()), }) + if err != nil { - log.Printf("[ERROR] Error deleting Appstream Stack: %s", err) + log.Printf("[ERROR] Error deleting Appstream stack: %s", err) return err } log.Printf("[DEBUG] %s", resp) @@ -315,66 +446,18 @@ func resourceAppstreamStackDelete(d *schema.ResourceData, meta interface{}) erro } -func expandStorageConnectorConfigs(storageConnectorConfigs []interface{}) []*appstream.StorageConnector { - storageConnectorConfig := []*appstream.StorageConnector{} +func expandAccessEndpointConfigs(accessEndpointConfigs []interface{}) []*appstream.AccessEndpoint { + accessEndpointConfig := []*appstream.AccessEndpoint{} - for _, raw := range storageConnectorConfigs { + for _, raw := range accessEndpointConfigs { configAttributes := raw.(map[string]interface{}) - configConnectorType := configAttributes["connector_type"].(string) - config := &appstream.StorageConnector{ - ConnectorType: aws.String(configConnectorType), + configEndpointType := configAttributes["endpoint_type"].(string) + configVpceId := configAttributes["vpce_id"].(string) + config := &appstream.AccessEndpoint{ + EndpointType: aws.String(configEndpointType), + VpceId: aws.String(configVpceId), } - storageConnectorConfig = append(storageConnectorConfig, config) - } - return storageConnectorConfig -} - -func expandUserSettingConfigs(userSettingConfigs []interface{}) []*appstream.UserSetting { - userSettingConfig := []*appstream.UserSetting{} - - for _, raw := range userSettingConfigs { - configAttributes := raw.(map[string]interface{}) - configFileDownload := configAttributes["file_download"].(string) - configFileUpload := configAttributes["file_upload"].(string) - configCopyFromLocal := configAttributes["copy_from_local"].(string) - configCopytoLocal := configAttributes["copy_to_local"].(string) - configAllowLocalPrint := configAttributes["allow_local_device_printing"].(string) - if configAttributes["file_download"] != nil { - config := &appstream.UserSetting{ - Action: aws.String("FILE_DOWNLOAD"), - Permission: aws.String(configFileDownload), - } - userSettingConfig = append(userSettingConfig, config) - } - if configAttributes["file_upload"] != nil { - config := &appstream.UserSetting{ - Action: aws.String("FILE_UPLOAD"), - Permission: aws.String(configFileUpload), - } - userSettingConfig = append(userSettingConfig, config) - } - if configAttributes["copy_from_local"] != nil { - config := &appstream.UserSetting{ - Action: aws.String("CLIPBOARD_COPY_FROM_LOCAL_DEVICE"), - Permission: aws.String(configCopyFromLocal), - } - userSettingConfig = append(userSettingConfig, config) - } - if configAttributes["copy_to_local"] != nil { - config := &appstream.UserSetting{ - Action: aws.String("CLIPBOARD_COPY_TO_LOCAL_DEVICE"), - Permission: aws.String(configCopytoLocal), - } - userSettingConfig = append(userSettingConfig, config) - } - if configAttributes["allow_local_device_printing"] != nil { - config := &appstream.UserSetting{ - Action: aws.String("PRINTING_TO_LOCAL_DEVICE"), - Permission: aws.String(configAllowLocalPrint), - } - userSettingConfig = append(userSettingConfig, config) - } - + accessEndpointConfig = append(accessEndpointConfig, config) } - return userSettingConfig + return accessEndpointConfig } diff --git a/appstream/resource_stack_attachment.go b/appstream/resource_stack_attachment.go new file mode 100644 index 0000000..0f32465 --- /dev/null +++ b/appstream/resource_stack_attachment.go @@ -0,0 +1,166 @@ +package appstream + +import ( + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAppstreamStackAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceAppstreamStackAttachmentCreate, + Read: resourceAppstreamStackAttachmentRead, + Update: resourceAppstreamStackAttachmentUpdate, + Delete: resourceAppstreamStackAttachmentDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "appstream_stack_id": { + Type: schema.TypeString, + Required: true, + }, + + "appstream_fleet_id": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAppstreamStackAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + svc := meta.(*AWSClient).appstreamconn + AssociateFleetInputOpts := &appstream.AssociateFleetInput{} + + if stack, ok := d.GetOk("appstream_stack_id"); ok { + AssociateFleetInputOpts.StackName = aws.String(stack.(string)) + } + + if fleet, ok := d.GetOk("appstream_fleet_id"); ok { + AssociateFleetInputOpts.FleetName = aws.String(fleet.(string)) + } + + log.Printf("[DEBUG] Run configuration: %s", AssociateFleetInputOpts) + + resp, err := svc.AssociateFleet(AssociateFleetInputOpts) + if err != nil { + log.Printf("[ERROR] Error associating Appstream Fleet to Stack: %s", err) + return err + } + + log.Printf("[DEBUG] Appstream Fleet associated to Stack %s ", resp) + + d.SetId(fmt.Sprintf("%s_%s", *AssociateFleetInputOpts.StackName, *AssociateFleetInputOpts.FleetName)) + + return resourceAppstreamStackAttachmentRead(d, meta) +} + +func resourceAppstreamStackAttachmentRead(d *schema.ResourceData, meta interface{}) error { + svc := meta.(*AWSClient).appstreamconn + + AssociationId := strings.Split(d.Id(), "_") + + resp, err := svc.ListAssociatedFleets(&appstream.ListAssociatedFleetsInput{ + StackName: &AssociationId[0], + }) + + if err != nil { + log.Printf("[ERROR] Error describing associations: %s", err) + return err + } + + stack := AssociationId[0] + for _, fleet := range resp.Names { + d.Set("appstream_stack_id", stack) + d.Set("appstream_fleet_id", fleet) + + return nil + } + + d.SetId("") + + return nil +} + +func resourceAppstreamStackAttachmentUpdate(d *schema.ResourceData, meta interface{}) error { + svc := meta.(*AWSClient).appstreamconn + DisassociateFleetInputOpts := &appstream.DisassociateFleetInput{} + AssociateFleetInputOpts := &appstream.AssociateFleetInput{} + + AssociationId := strings.Split(d.Id(), "_") + DisassociateFleetInputOpts.StackName = &AssociationId[0] + DisassociateFleetInputOpts.FleetName = &AssociationId[1] + + AssociateFleetInputOpts.StackName = DisassociateFleetInputOpts.StackName + AssociateFleetInputOpts.FleetName = DisassociateFleetInputOpts.FleetName + + // d.Partial(true) + + if d.HasChange("appstream_stack_id") { + // d.SetPartial("appstream_stack_id") + log.Printf("[DEBUG] Modify appstream association") + appstream_stack_id := d.Get("appstream_stack_id").(string) + AssociateFleetInputOpts.StackName = aws.String(appstream_stack_id) + } + + if d.HasChange("appstream_fleet_id") { + // d.SetPartial("appstream_fleet_id") + log.Printf("[DEBUG] Modify appstream association") + appstream_fleet_id := d.Get("appstream_fleet_id").(string) + AssociateFleetInputOpts.FleetName = aws.String(appstream_fleet_id) + } + + if d.HasChanges("appstream_stack_id", "appstream_fleet_id") { + dis_resp, dis_err := svc.DisassociateFleet(&appstream.DisassociateFleetInput{ + StackName: aws.String(*DisassociateFleetInputOpts.StackName), + FleetName: aws.String(*DisassociateFleetInputOpts.FleetName), + }) + + if dis_err != nil { + log.Printf("[ERROR] Error disassociating Appstream Fleet from Stack: %s", dis_err) + return dis_err + } + + log.Printf("[DEBUG] %s", dis_resp) + + ass_resp, ass_err := svc.AssociateFleet(AssociateFleetInputOpts) + if ass_err != nil { + log.Printf("[ERROR] Error associating Appstream Fleet to Stack: %s", ass_err) + return ass_err + } + + d.SetId(fmt.Sprintf("%s_%s", *AssociateFleetInputOpts.StackName, *AssociateFleetInputOpts.FleetName)) + + log.Printf("[DEBUG] Appstream Fleet associated to Stack %s ", ass_resp) + } + + // d.Partial(false) + + return nil +} + +func resourceAppstreamStackAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + svc := meta.(*AWSClient).appstreamconn + + AssociationId := strings.Split(d.Id(), "_") + + resp, err := svc.DisassociateFleet(&appstream.DisassociateFleetInput{ + StackName: aws.String(AssociationId[0]), + FleetName: aws.String(AssociationId[1]), + }) + + if err != nil { + log.Printf("[ERROR] Error disassociating Appstream Fleet from Stack: %s", err) + return err + } + + log.Printf("[DEBUG] %s", resp) + + return nil +} diff --git a/appstream/resource_usage_report_subscription.go b/appstream/resource_usage_report_subscription.go new file mode 100644 index 0000000..f41ee7f --- /dev/null +++ b/appstream/resource_usage_report_subscription.go @@ -0,0 +1,93 @@ +package appstream + +import ( + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAppstreamUsageReportSubscription() *schema.Resource { + return &schema.Resource{ + Create: resourceAppstreamUsageReportSubscriptionCreate, + Read: resourceAppstreamUsageReportSubscriptionRead, + Delete: resourceAppstreamUsageReportSubscriptionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "s3_bucket_name": { + Type: schema.TypeString, + Computed: true, + }, + "schedule": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAppstreamUsageReportSubscriptionCreate(d *schema.ResourceData, meta interface{}) error { + + svc := meta.(*AWSClient).appstreamconn + + CreateUsageReportSubscriptionInputOpts := &appstream.CreateUsageReportSubscriptionInput{} + + log.Printf("[DEBUG] Run configuration: %s", CreateUsageReportSubscriptionInputOpts) + + resp, err := svc.CreateUsageReportSubscription(CreateUsageReportSubscriptionInputOpts) + + if err != nil { + log.Printf("[ERROR] Error creating Appstream Usage Report: %s", err) + return err + } + + log.Printf("[DEBUG] Usage Report created %s", resp) + + d.SetId(*resp.S3BucketName) + d.Set("s3_bucket_name", resp.S3BucketName) + d.Set("schedule", resp.Schedule) + + return nil +} + +func resourceAppstreamUsageReportSubscriptionRead(d *schema.ResourceData, meta interface{}) error { + + svc := meta.(*AWSClient).appstreamconn + + resp, err := svc.DescribeUsageReportSubscriptions(&appstream.DescribeUsageReportSubscriptionsInput{}) + if err != nil { + log.Printf("[ERROR] Error describing Appstream Usage Reports Subscription: %s", err) + return err + } + for _, v := range resp.UsageReportSubscriptions { + + if aws.StringValue(v.S3BucketName) == d.Get("s3_bucket_name") { + d.Set("s3_bucket_name", v.S3BucketName) + d.Set("schedule", v.Schedule) + + return nil + } + } + + d.SetId("") + return nil + +} + +func resourceAppstreamUsageReportSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { + svc := meta.(*AWSClient).appstreamconn + + del, err := svc.DeleteUsageReportSubscription(&appstream.DeleteUsageReportSubscriptionInput{}) + + if err != nil { + log.Printf("[ERROR] Error deleting Appstream Usage Report Subscription: %s", err) + return err + } + log.Printf("[DEBUG] %s", del) + + return nil +} diff --git a/appstream/utils.go b/appstream/utils.go new file mode 100644 index 0000000..7236206 --- /dev/null +++ b/appstream/utils.go @@ -0,0 +1,143 @@ +package appstream + +import ( + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/appstream" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func expandStringList(configured []interface{}) []*string { + vs := make([]*string, 0, len(configured)) + for _, v := range configured { + val, ok := v.(string) + if ok && val != "" { + vs = append(vs, aws.String(v.(string))) + } + } + return vs +} + +func expandStringSet(configured *schema.Set) []*string { + return expandStringList(configured.List()) +} + +func flattenStringList(list []*string) []interface{} { + vs := make([]interface{}, 0, len(list)) + for _, v := range list { + vs = append(vs, *v) + } + return vs +} + +func flattenStringSet(list []*string) *schema.Set { + return schema.NewSet(schema.HashString, flattenStringList(list)) +} + +func expandAccessEndpointsConfigs(accessEndpointsConfigs []interface{}) []*appstream.AccessEndpoint { + accessEndpointsConfig := []*appstream.AccessEndpoint{} + + for _, raw := range accessEndpointsConfigs { + configAttributes := raw.(map[string]interface{}) + + config := &appstream.AccessEndpoint{} + if v, ok := configAttributes["endpoint_type"]; ok { + config.EndpointType = aws.String(v.(string)) + } + + if v, ok := configAttributes["vpce_id"]; ok { + config.VpceId = aws.String(v.(string)) + } + + accessEndpointsConfig = append(accessEndpointsConfig, config) + } + + return accessEndpointsConfig +} + +func expandStorageConnectorConfigs(storageConnectorConfigs []interface{}) []*appstream.StorageConnector { + storageConnectorConfig := []*appstream.StorageConnector{} + + for _, raw := range storageConnectorConfigs { + configAttributes := raw.(map[string]interface{}) + + config := &appstream.StorageConnector{} + if v, ok := configAttributes["connector_type"]; ok { + config.ConnectorType = aws.String(v.(string)) + } + + log.Printf("[***] %s", configAttributes["domains"]) + if v, ok := configAttributes["domains"]; ok && len(v.([]interface{})) > 0 { + config.Domains = v.([]*string) + } + + if v, ok := configAttributes["resource_identifier"]; ok && len(v.(string)) > 0 { + config.ResourceIdentifier = aws.String(v.(string)) + } + + storageConnectorConfig = append(storageConnectorConfig, config) + } + + return storageConnectorConfig +} + +func expandUserSettingConfigs(userSettingConfigs []interface{}) []*appstream.UserSetting { + userSettingConfig := []*appstream.UserSetting{} + + for _, raw := range userSettingConfigs { + configAttributes := raw.(map[string]interface{}) + + config := &appstream.UserSetting{} + if v, ok := configAttributes["action"]; ok { + config.Action = aws.String(v.(string)) + } + + if v, ok := configAttributes["permission"]; ok { + config.Permission = aws.String(v.(string)) + } + + userSettingConfig = append(userSettingConfig, config) + } + + return userSettingConfig +} + +func expandApplicationSettings(applicationSettings []interface{}) *appstream.ApplicationSettings { + ApplicationSettings := &appstream.ApplicationSettings{} + attr := applicationSettings[0].(map[string]interface{}) + + if v, ok := attr["enabled"]; ok { + ApplicationSettings.Enabled = aws.Bool(v.(bool)) + } + + if v, ok := attr["settings_group"]; ok { + ApplicationSettings.SettingsGroup = aws.String(v.(string)) + } + + return ApplicationSettings +} + +func expandVpcConfigs(vpcConfigs []interface{}) *appstream.VpcConfig { + VpcConfigConfig := &appstream.VpcConfig{} + attr := vpcConfigs[0].(map[string]interface{}) + + if v, ok := attr["security_group_ids"]; ok { + VpcConfigConfig.SecurityGroupIds = expandStringSet(v.(*schema.Set)) + } + + if v, ok := attr["subnet_ids"]; ok { + VpcConfigConfig.SubnetIds = expandStringSet(v.(*schema.Set)) + } + + return VpcConfigConfig +} + +func expandTags(data_tags map[string]interface{}) map[string]string { + attr := make(map[string]string) + for k, v := range data_tags { + attr[k] = v.(string) + } + + return attr +} diff --git a/build.groovy b/build.groovy deleted file mode 100644 index a04f1bd..0000000 --- a/build.groovy +++ /dev/null @@ -1,25 +0,0 @@ - -node ("master") { - stage("checkout") { - checkout scm - } - - stage("get") { - withEnv(["PATH=${env.PATH}:/usr/lib/go-1.10/bin", - "GOPATH=${env.HOME}/go", - "GOBIN=${env.HOME}/go/bin", - ]){ - sh "go get" - } - } - - stage("build") { - withEnv([ - "PATH=${env.PATH}:/usr/lib/go-1.10/bin", - "GOPATH=${env.HOME}/go", - "GOBIN=${env.HOME}/go/bin", - ]){ - sh "go build -o ${env.HOME}/.terraform.d/plugins/terraform-provider-appstream_v0.0.1" - } - } -} \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..fd9c5f7 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,42 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "appstream Provider" +subcategory: "" +description: |- + +--- + +# appstream Provider + + + + + + +## Schema + +### Required + +- **region** (String) Region + +### Optional + +- **access_key** (String) Access key id +- **assume_role** (Block Set, Max: 1) (see [below for nested schema](#nestedblock--assume_role)) +- **profile** (String) The profile for API operations. If not set, the default profile +created with `aws configure` will be used. +- **secret_key** (String) Secret key id +- **shared_credentials_file** (String) The path to the shared credentials file. If not set +this defaults to ~/.aws/credentials. +- **token** (String) Security token + + +### Nested Schema for `assume_role` + +Optional: + +- **duration_seconds** (Number) The duration, in seconds, of the role session. The value specified can can range from 900 seconds (15 minutes) up to the maximum session duration that is set for the role. The maximum session duration setting can have a value from 1 hour to 12 hours. If you specify a value higher than this setting, the operation fails. For example, if you specify a session duration of 12 hours, but your administrator set the maximum session duration to 6 hours, your operation fails. +- **external_id** (String) The external ID to use when assuming the role. If omitted, no external ID is passed to the AssumeRole call. +- **policy** (String) The permissions applied when assuming a role. You cannot use, this policy to grant further permissions that are in excess to those of the, role that is being assumed. +- **role_arn** (String) The ARN of an IAM role to assume prior to making API calls. +- **session_name** (String) The session name to use when assuming the role. If omitted, no session name is passed to the AssumeRole call. diff --git a/docs/resources/fleet.md b/docs/resources/fleet.md new file mode 100644 index 0000000..3721dfd --- /dev/null +++ b/docs/resources/fleet.md @@ -0,0 +1,68 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "appstream_fleet Resource - terraform-provider-appstream" +subcategory: "" +description: |- + +--- + +# appstream_fleet (Resource) + + + + + + +## Schema + +### Required + +- **compute_capacity** (Block List, Min: 1) (see [below for nested schema](#nestedblock--compute_capacity)) +- **instance_type** (String) +- **name** (String) + +### Optional + +- **description** (String) +- **disconnect_timeout** (Number) +- **display_name** (String) +- **domain_info** (Block List) (see [below for nested schema](#nestedblock--domain_info)) +- **enable_default_internet_access** (Boolean) +- **fleet_type** (String) +- **iam_role_arn** (String) +- **id** (String) The ID of this resource. +- **image_arn** (String) +- **image_name** (String) +- **max_user_duration** (Number) +- **stack_name** (String) +- **state** (String) +- **stream_view** (String) +- **tags** (Map of String) +- **vpc_config** (Block List) (see [below for nested schema](#nestedblock--vpc_config)) + + +### Nested Schema for `compute_capacity` + +Required: + +- **desired_instances** (Number) + + + +### Nested Schema for `domain_info` + +Optional: + +- **directory_name** (String) +- **organizational_unit_distinguished_name** (String) + + + +### Nested Schema for `vpc_config` + +Optional: + +- **security_group_ids** (Set of String) +- **subnet_ids** (Set of String) + + diff --git a/docs/resources/image_builder.md b/docs/resources/image_builder.md new file mode 100644 index 0000000..20b6121 --- /dev/null +++ b/docs/resources/image_builder.md @@ -0,0 +1,63 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "appstream_image_builder Resource - terraform-provider-appstream" +subcategory: "" +description: |- + +--- + +# appstream_image_builder (Resource) + + + + + + +## Schema + +### Required + +- **instance_type** (String) +- **name** (String) + +### Optional + +- **access_endpoints** (Block Set) (see [below for nested schema](#nestedblock--access_endpoints)) +- **appstream_agent_version** (String) +- **description** (String) +- **display_name** (String) +- **domain_info** (Block List) (see [below for nested schema](#nestedblock--domain_info)) +- **enable_default_internet_access** (Boolean) +- **id** (String) The ID of this resource. +- **image_arn** (String) +- **image_name** (String) +- **state** (String) +- **vpc_config** (Block List) (see [below for nested schema](#nestedblock--vpc_config)) + + +### Nested Schema for `access_endpoints` + +Required: + +- **endpoint_type** (String) +- **vpce_id** (String) + + + +### Nested Schema for `domain_info` + +Optional: + +- **directory_name** (String) +- **organizational_unit_distinguished_name** (String) + + + +### Nested Schema for `vpc_config` + +Optional: + +- **security_group_ids** (String) +- **subnet_ids** (String) + + diff --git a/docs/resources/stack.md b/docs/resources/stack.md new file mode 100644 index 0000000..f94def3 --- /dev/null +++ b/docs/resources/stack.md @@ -0,0 +1,75 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "appstream_stack Resource - terraform-provider-appstream" +subcategory: "" +description: |- + +--- + +# appstream_stack (Resource) + + + + + + +## Schema + +### Required + +- **name** (String) + +### Optional + +- **access_endpoints** (Block Set) (see [below for nested schema](#nestedblock--access_endpoints)) +- **application_settings** (Block List) (see [below for nested schema](#nestedblock--application_settings)) +- **description** (String) +- **display_name** (String) +- **embed_host_domains** (Set of String) +- **feedback_url** (String) +- **id** (String) The ID of this resource. +- **redirect_url** (String) +- **storage_connectors** (Block Set) (see [below for nested schema](#nestedblock--storage_connectors)) +- **tags** (Map of String) +- **user_settings** (Block Set) (see [below for nested schema](#nestedblock--user_settings)) + + +### Nested Schema for `access_endpoints` + +Required: + +- **endpoint_type** (String) +- **vpce_id** (String) + + + +### Nested Schema for `application_settings` + +Required: + +- **enabled** (Boolean) +- **settings_group** (String) + + + +### Nested Schema for `storage_connectors` + +Required: + +- **connector_type** (String) + +Optional: + +- **domains** (List of String) +- **resource_identifier** (String) + + + +### Nested Schema for `user_settings` + +Required: + +- **action** (String) +- **permission** (String) + + diff --git a/docs/resources/stack_attachment.md b/docs/resources/stack_attachment.md new file mode 100644 index 0000000..bc82755 --- /dev/null +++ b/docs/resources/stack_attachment.md @@ -0,0 +1,27 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "appstream_stack_attachment Resource - terraform-provider-appstream" +subcategory: "" +description: |- + +--- + +# appstream_stack_attachment (Resource) + + + + + + +## Schema + +### Required + +- **appstream_fleet_id** (String) +- **appstream_stack_id** (String) + +### Optional + +- **id** (String) The ID of this resource. + + diff --git a/docs/resources/usage_report_subscription.md b/docs/resources/usage_report_subscription.md new file mode 100644 index 0000000..c63426e --- /dev/null +++ b/docs/resources/usage_report_subscription.md @@ -0,0 +1,27 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "appstream_usage_report_subscription Resource - terraform-provider-appstream" +subcategory: "" +description: |- + +--- + +# appstream_usage_report_subscription (Resource) + + + + + + +## Schema + +### Optional + +- **id** (String) The ID of this resource. + +### Read-Only + +- **s3_bucket_name** (String) +- **schedule** (String) + + diff --git a/examples/appstream.tf b/examples/appstream.tf index 8fcb53d..4d72819 100644 --- a/examples/appstream.tf +++ b/examples/appstream.tf @@ -1,30 +1,99 @@ +variable "region" { + type = string + default = "eu-west-1" +} + +variable "assume_role_arn" { + type = string +} + provider "appstream" { - version = "v1.0.8" + region = var.region + assume_role { + role_arn = var.assume_role_arn + } +} + +provider "aws" { + region = var.region assume_role { role_arn = var.assume_role_arn } - region = var.region_primary } +variable "tags" { + description = "A map of tags to add to all resources" + default = {} +} + +locals { + appstream_tags = { + ProviderVersion = "v2.0.0" + Env = "test" + } +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + + name = "appstream-test-vpc" + cidr = "10.0.0.0/16" + + azs = ["eu-central-1a", "eu-central-1b", "eu-central-1c"] + private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] + enable_nat_gateway = true + enable_vpn_gateway = false + + tags = { + Terraform = "true" + Environment = "dev" + } +} + +resource "aws_security_group" "appstream_security_group" { + name = "appstream-test-security-group" + vpc_id = module.vpc.vpc_id +} + +resource "aws_security_group_rule" "allow_all_outbound" { + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = aws_security_group.appstream_security_group.id +} + +resource "aws_security_group_rule" "allow_all_inbbound" { + type = "ingress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = aws_security_group.appstream_security_group.id +} + +/* resource "appstream_image_builder" "test-image-builder" { + count = 0 name = "test-image-builder" - appstream_agent_version = "LATEST" + appstream_agent_version = "05-17-2021" description = "test image builder" display_name = "test-image-builder" - enable_default_internet_access = true - image_name = "Base-Image-Builder-05-02-2018" + enable_default_internet_access = false + image_name = "AppStream-WinServer2019-06-01-2021" instance_type = "stream.standard.large" vpc_config { - security_group_ids = "sg-b5af81d3" - subnet_ids = "subnet-7a5f4b51" + security_group_ids = aws_security_group.appstream_security_group.id + subnet_ids = module.vpc.private_subnets[0] } state = "RUNNING" } - - -resource "appstream_stack" "test-stack" { - name = "test-stack" +*/ +resource "appstream_stack" "test_stack" { + name = "appstream-test-stack" description = "appstream test stack" display_name = "test-stack" feedback_url = "http://example1.com" @@ -32,33 +101,49 @@ resource "appstream_stack" "test-stack" { storage_connectors { connector_type = "HOMEFOLDERS" } - tags { + tags = { Env = "lab" - Role = "appstream-stack" + Role = "appstream-test-stack" + Terraform = "true" + Environment = "dev" + Project = "ISD" } } -resource "appstream_fleet" "test-fleet" { +resource "appstream_fleet" "test_fleet" { name = "test-fleet" - stack_name = appstream_stack.test-stack.name + stack_name = appstream_stack.test_stack.name compute_capacity { desired_instances = 1 } description = "test fleet" disconnect_timeout = 300 display_name = "test-fleet" - enable_default_internet_access = true + enable_default_internet_access = false fleet_type = "ON_DEMAND" - image_name = "arn:aws:appstream:eu-west-1:1231241241:image/Base-Image-Builder-05-02-2018" +# Use image ARN when you share images betwen multiple accounts in an organization +# Build your image in a shared account. +# image_arn = "arn:aws:appstream:eu-west-1:1231241241:image/Base-Image-Builder-05-02-2018" + image_name = "AppStream-WinServer2019-06-01-2021" instance_type = "stream.standard.large" max_user_duration = 600 vpc_config { - security_group_ids = "sg-b5af81d3" - subnet_ids = "subnet-7a5f4b51,subnet-7a5f1231" + security_group_ids = [aws_security_group.appstream_security_group.id] +# subnet_ids = join(", ", module.vpc.private_subnets) + subnet_ids = module.vpc.private_subnets } - tags { + tags = { Env = "lab" - Role = "appstream-fleet" + Role = "appstream-test-fleet" + Terraform = "true" + Environment = "dev" + Project = "ISD" } state = "RUNNING" } + +resource "appstream_stack_attachment" "test_attachment" { + appstream_stack_id = appstream_stack.test_stack.name + appstream_fleet_id = appstream_fleet.test_fleet.name +} + diff --git a/examples/versions.tf b/examples/versions.tf new file mode 100644 index 0000000..b9cac83 --- /dev/null +++ b/examples/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + appstream = { + source = "arnvid/appstream" + version = "2.0.0" + } + aws = { + source = "hashicorp/aws" + } + } + required_version = ">= 1.0" +} diff --git a/go.mod b/go.mod index 1f1e9ad..c55667a 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,17 @@ module github.com/arnvid/terraform-provider-appstream -go 1.14 +go 1.16 require ( - github.com/aws/aws-sdk-go v1.32.1 - github.com/hashicorp/aws-sdk-go-base v0.5.0 - github.com/hashicorp/terraform v0.12.26 // indirect - github.com/hashicorp/terraform-plugin-sdk v1.13.0 + github.com/aws/aws-sdk-go v1.38.63 + github.com/hashicorp/aws-sdk-go-base v0.7.1 + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/terraform-plugin-go v0.3.0 // indirect + github.com/hashicorp/terraform-plugin-sdk v1.17.2 // indirect + github.com/hashicorp/terraform-plugin-sdk/v2 v2.6.1 + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/go-testing-interface v1.14.1 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect ) diff --git a/main.go b/main.go index 2c71977..c5f7d53 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,20 @@ package main import ( - "github.com/hashicorp/terraform-plugin-sdk/plugin" - "github.com/arnvid/terraform-provider-appstream/appstream" + // "context" + // "flag" + // "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" + + "github.com/arnvid/terraform-provider-appstream/appstream" ) func main() { - plugin.Serve(&plugin.ServeOpts{ - ProviderFunc: appstream.Provider}) + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: func() *schema.Provider { + return appstream.Provider() + }, + }) } -