diff --git a/docs/resources/user_group.md b/docs/resources/user_group.md index 7dcf1e307..77abb57ef 100644 --- a/docs/resources/user_group.md +++ b/docs/resources/user_group.md @@ -73,6 +73,10 @@ resource "harness_user_group" "example" { actions = ["UPDATE", "DELETE"] } + template { + actions = ["CREATE", "READ", "UPDATE", "DELETE"] + } + workflow { actions = ["UPDATE", "DELETE"] filters = ["NON_PRODUCTION_WORKFLOWS", ] @@ -151,6 +155,7 @@ Optional: - **pipeline** (Block Set) Permission configuration to perform actions against pipelines. (see [below for nested schema](#nestedblock--permissions--app_permissions--pipeline)) - **provisioner** (Block Set) Permission configuration to perform actions against provisioners. (see [below for nested schema](#nestedblock--permissions--app_permissions--provisioner)) - **service** (Block Set) Permission configuration to perform actions against services. (see [below for nested schema](#nestedblock--permissions--app_permissions--service)) +- **template** (Block Set) Permission configuration to perform actions against templates. (see [below for nested schema](#nestedblock--permissions--app_permissions--template)) - **workflow** (Block Set) Permission configuration to perform actions against workflows. (see [below for nested schema](#nestedblock--permissions--app_permissions--workflow)) @@ -233,6 +238,19 @@ Optional: - **service_ids** (Set of String) The service IDs to which the permission applies. Leave empty to apply to all services. + +### Nested Schema for `permissions.app_permissions.template` + +Required: + +- **actions** (Set of String) The actions allowed to be performed. Valid options are CREATE, READ, UPDATE, DELETE + +Optional: + +- **app_ids** (Set of String) The application IDs to which the permission applies. Leave empty to apply to all applications. +- **template_ids** (Set of String) The template IDs to which the permission applies. Leave empty to apply to all environments. + + ### Nested Schema for `permissions.app_permissions.workflow` diff --git a/examples/resources/harness_user_group/resource.tf b/examples/resources/harness_user_group/resource.tf index 524c327d9..cd28d6046 100644 --- a/examples/resources/harness_user_group/resource.tf +++ b/examples/resources/harness_user_group/resource.tf @@ -58,6 +58,10 @@ resource "harness_user_group" "example" { actions = ["UPDATE", "DELETE"] } + template { + actions = ["CREATE", "READ", "UPDATE", "DELETE"] + } + workflow { actions = ["UPDATE", "DELETE"] filters = ["NON_PRODUCTION_WORKFLOWS", ] diff --git a/internal/provider/resource_cloudprovider_tanzu.go b/internal/provider/resource_cloudprovider_tanzu.go index 8a7da5430..76d46eaf6 100644 --- a/internal/provider/resource_cloudprovider_tanzu.go +++ b/internal/provider/resource_cloudprovider_tanzu.go @@ -138,30 +138,3 @@ func resourceCloudProviderTanzuCreateOrUpdate(ctx context.Context, d *schema.Res return readCloudProviderTanzu(c, d, cp) } - -func resourceCloudProviderTanzuUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - c := meta.(*api.Client) - - cp := cac.NewEntity(cac.ObjectTypes.AzureCloudProvider).(*cac.PcfCloudProvider) - cp.Name = d.Get("name").(string) - cp.EndpointUrl = d.Get("endpoint").(string) - cp.SkipValidation = d.Get("skip_validation").(bool) - cp.Username = d.Get("username").(string) - - if attr := d.Get("username_secret_name").(string); attr != "" { - cp.UsernameSecretId = &cac.SecretRef{ - Name: attr, - } - } - - cp.Password = &cac.SecretRef{ - Name: d.Get("password_secret_name").(string), - } - - _, err := c.ConfigAsCode().UpsertPcfCloudProvider(cp) - if err != nil { - return diag.FromErr(err) - } - - return nil -} diff --git a/internal/provider/resource_git_connector_test.go b/internal/provider/resource_git_connector_test.go index 88c267d5f..254215a43 100644 --- a/internal/provider/resource_git_connector_test.go +++ b/internal/provider/resource_git_connector_test.go @@ -249,29 +249,6 @@ func testAccResourceGitConnector(name string, generateWebhook bool, withCommitDe `, name, generateWebhook, commitDetails, delegateSelectors) } -func testAccResourceGitConnector_invalid_urltype(name string) string { - return fmt.Sprintf(` - data "harness_secret_manager" "test" { - default = true - } - - resource "harness_encrypted_text" "test" { - name = "%[1]s" - value = "foo" - secret_manager_id = data.harness_secret_manager.test.id - } - - resource "harness_git_connector" "test" { - name = "%[1]s" - url = "https://github.com/micahlmartin/harness-demo" - branch = "master" - password_secret_id = harness_encrypted_text.test.id - url_type = "badvalue" - username = "someuser" - } -`, name) -} - // func testAccResourceEncryptedText_UsageScopes(name string) string { // // nonprod := // return fmt.Sprintf(` diff --git a/internal/provider/resource_infrastructure_definition_test.go b/internal/provider/resource_infrastructure_definition_test.go index 981813285..2c81974bd 100644 --- a/internal/provider/resource_infrastructure_definition_test.go +++ b/internal/provider/resource_infrastructure_definition_test.go @@ -357,26 +357,6 @@ func testAccInfraDefK8s(name string) string { `, name) } -func testAccInfraDefAws(name string) string { - return fmt.Sprintf(` - resource "harness_cloudprovider_aws" "test" { - name = "%[1]s" - // delegate_selector = "aws" - - } - - resource "harness_application" "test" { - name = "%[1]s" - } - - resource "harness_environment" "test" { - name = "%[1]s" - app_id = harness_application.test.id - type = "NON_PROD" - } -`, name) -} - func testAccResourceInfraDefEnvironment(name string) string { return fmt.Sprintf(` resource "harness_application" "test" { diff --git a/internal/provider/resource_service_aws_codedeploy.go b/internal/provider/resource_service_aws_codedeploy.go index 4daa25e03..5db677647 100644 --- a/internal/provider/resource_service_aws_codedeploy.go +++ b/internal/provider/resource_service_aws_codedeploy.go @@ -93,30 +93,3 @@ func resourceAWSCodeDeployServiceCreateOrUpdate(ctx context.Context, d *schema.R return readServiceCodeDeploy(d, newSvc) } - -func resourceAWSCodeDeployServiceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - c := meta.(*api.Client) - - // Setup the object to create - svcInput := &cac.Service{ - Name: d.Get("name").(string), - ArtifactType: cac.ArtifactTypes.AWSCodeDeploy, - DeploymentType: cac.DeploymentTypes.AWSCodeDeploy, - ApplicationId: d.Get("app_id").(string), - Description: d.Get("description").(string), - } - - if vars := d.Get("variable"); vars != nil { - svcInput.ConfigVariables = expandServiceVariables(vars.(*schema.Set).List()) - } - - // Create Service - newSvc, err := c.ConfigAsCode().UpsertService(svcInput) - if err != nil { - return diag.FromErr(err) - } - - d.SetId(newSvc.Id) - - return nil -} diff --git a/internal/provider/resource_service_aws_lambda.go b/internal/provider/resource_service_aws_lambda.go index 42aaf7739..00257f432 100644 --- a/internal/provider/resource_service_aws_lambda.go +++ b/internal/provider/resource_service_aws_lambda.go @@ -93,30 +93,3 @@ func resourceAWSLambdaServiceCreateOrUpdate(ctx context.Context, d *schema.Resou return readServiceAwsLambda(d, newSvc) } - -func resourceAWSLambdaServiceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - c := meta.(*api.Client) - - // Setup the object to create - svcInput := &cac.Service{ - Name: d.Get("name").(string), - ArtifactType: cac.ArtifactTypes.AWSLambda, - DeploymentType: cac.DeploymentTypes.AWSLambda, - ApplicationId: d.Get("app_id").(string), - Description: d.Get("description").(string), - } - - if vars := d.Get("variable"); vars != nil { - svcInput.ConfigVariables = expandServiceVariables(vars.(*schema.Set).List()) - } - - // Create Service - newSvc, err := c.ConfigAsCode().UpsertService(svcInput) - if err != nil { - return diag.FromErr(err) - } - - d.SetId(newSvc.Id) - - return nil -} diff --git a/internal/provider/resource_service_winrm.go b/internal/provider/resource_service_winrm.go index b4019c2b5..cc06d4ffc 100644 --- a/internal/provider/resource_service_winrm.go +++ b/internal/provider/resource_service_winrm.go @@ -105,30 +105,3 @@ func resourceWinRMServiceCreateOrUpdate(ctx context.Context, d *schema.ResourceD return readServiceWinRM(d, newSvc) } - -func resourceWinRMServiceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - c := meta.(*api.Client) - - // Setup the object to create - svcInput := &cac.Service{ - Name: d.Get("name").(string), - ArtifactType: cac.ArtifactType(d.Get("artifact_type").(string)), - DeploymentType: cac.DeploymentTypes.WinRM, - ApplicationId: d.Get("app_id").(string), - Description: d.Get("description").(string), - } - - if vars := d.Get("variable"); vars != nil { - svcInput.ConfigVariables = expandServiceVariables(vars.(*schema.Set).List()) - } - - // Create Service - newSvc, err := c.ConfigAsCode().UpsertService(svcInput) - if err != nil { - return diag.FromErr(err) - } - - d.SetId(newSvc.Id) - - return nil -} diff --git a/internal/provider/resource_user_group.go b/internal/provider/resource_user_group.go index a355bf847..a518816d6 100644 --- a/internal/provider/resource_user_group.go +++ b/internal/provider/resource_user_group.go @@ -326,6 +326,34 @@ func resourceUserGroup() *schema.Resource { }, }, }, + "template": { + Description: "Permission configuration to perform actions against templates.", + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "app_ids": { + Description: "The application IDs to which the permission applies. Leave empty to apply to all applications.", + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "template_ids": { + Description: "The template IDs to which the permission applies. Leave empty to apply to all environments.", + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "actions": { + Description: fmt.Sprintf("The actions allowed to be performed. Valid options are %s", strings.Join(standardActions, ", ")), + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, }, }, }, @@ -507,6 +535,53 @@ func expandAccountPermissions(d []interface{}, input *graphql.UserGroupPermissio } } +func expandTemplateAppPermissions(d []interface{}, input *graphql.UserGroupPermissions) { + if len(d) == 0 { + return + } + + for _, v := range d { + permission := &graphql.AppPermission{ + PermissionType: graphql.AppPermissionTypes.Template, + Applications: &graphql.AppFilter{}, + Actions: []graphql.Action{}, + Templates: &graphql.TemplatePermissionFilter{}, + } + + permissionConfig := v.(map[string]interface{}) + if attr := permissionConfig["app_ids"]; attr != nil { + for _, appId := range attr.(*schema.Set).List() { + permission.Applications.AppIds = append(permission.Applications.AppIds, appId.(string)) + } + } + + if len(permission.Applications.AppIds) == 0 { + permission.Applications.FilterType = graphql.FilterTypes.All + } + + if attr := permissionConfig["actions"]; attr != nil { + for _, action := range attr.(*schema.Set).List() { + permission.Actions = append(permission.Actions, graphql.Action(action.(string))) + } + } + + if attr := permissionConfig["template_ids"]; attr != nil { + templateIds := attr.(*schema.Set).List() + if len(templateIds) > 0 { + tmplIds := []string{} + for _, templateId := range templateIds { + tmplIds = append(tmplIds, templateId.(string)) + } + permission.Templates.TemplateIds = tmplIds + } else { + permission.Templates.FilterType = graphql.FilterTypes.All + } + } + + input.AppPermissions = append(input.AppPermissions, permission) + } +} + func expandWorkflowAppPermissions(d []interface{}, input *graphql.UserGroupPermissions) { if len(d) == 0 { return @@ -862,6 +937,7 @@ func expandAppPermissions(d []interface{}, input *graphql.UserGroupPermissions) expandPipelineAppPermissions(appPermissionsConfig["pipeline"].(*schema.Set).List(), input) expandProvisionerAppPermissions(appPermissionsConfig["provisioner"].(*schema.Set).List(), input) expandServiceAppPermissions(appPermissionsConfig["service"].(*schema.Set).List(), input) + expandTemplateAppPermissions(appPermissionsConfig["template"].(*schema.Set).List(), input) expandWorkflowAppPermissions(appPermissionsConfig["workflow"].(*schema.Set).List(), input) } @@ -1093,6 +1169,26 @@ func flattenAppPermissionService(appPermissions *graphql.AppPermission) map[stri return results } +func flattenAppPermissionTemplate(appPermissions *graphql.AppPermission) map[string]interface{} { + results := map[string]interface{}{} + + if len(appPermissions.Applications.AppIds) > 0 { + results["app_ids"] = appPermissions.Applications.AppIds + } + + if len(appPermissions.Actions) > 0 { + results["actions"] = appPermissions.Actions + } + + if appPermissions.Templates != nil { + if len(appPermissions.Templates.TemplateIds) > 0 { + results["template_ids"] = appPermissions.Templates.TemplateIds + } + } + + return results +} + func flattenAppPermissionWorkflow(appPermissions *graphql.AppPermission) map[string]interface{} { results := map[string]interface{}{} @@ -1130,6 +1226,7 @@ func flattenAppPermissions(permissions *graphql.UserGroupPermissions) []interfac pipelines := []interface{}{} provisioners := []interface{}{} services := []interface{}{} + templates := []interface{}{} workflows := []interface{}{} for _, appPermission := range permissions.AppPermissions { @@ -1146,6 +1243,8 @@ func flattenAppPermissions(permissions *graphql.UserGroupPermissions) []interfac provisioners = append(provisioners, flattenAppPermissionProvisioner(appPermission)) case graphql.AppPermissionTypes.Service: services = append(services, flattenAppPermissionService(appPermission)) + case graphql.AppPermissionTypes.Template: + templates = append(templates, flattenAppPermissionTemplate(appPermission)) case graphql.AppPermissionTypes.Workflow: workflows = append(workflows, flattenAppPermissionWorkflow(appPermission)) default: @@ -1160,6 +1259,7 @@ func flattenAppPermissions(permissions *graphql.UserGroupPermissions) []interfac "pipeline": pipelines, "provisioner": provisioners, "service": services, + "template": templates, "workflow": workflows, }) } diff --git a/internal/provider/resource_user_group_test.go b/internal/provider/resource_user_group_test.go index 89dd39778..79e5d85f9 100644 --- a/internal/provider/resource_user_group_test.go +++ b/internal/provider/resource_user_group_test.go @@ -64,6 +64,11 @@ func TestAccResourceUserGroup_SAML(t *testing.T) { testAccUserGroupCreation(t, resourceName, expectedName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -85,6 +90,11 @@ func TestAccResourceUserGroup_NotificationsSettings(t *testing.T) { testAccUserGroupCreation(t, resourceName, expectedName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -106,6 +116,11 @@ func TestAccResourceUserGroup_AccountPermissions(t *testing.T) { testAccUserGroupCreation(t, resourceName, expectedName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -162,6 +177,11 @@ func TestAccResourceUserGroup_AppPermissions(t *testing.T) { testAccUserGroupCreation(t, resourceName, expectedName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -255,6 +275,10 @@ func testAccResourceUserGroupAppPermissions(name string) string { service { actions = ["UPDATE", "DELETE"] } + + template { + actions = ["CREATE", "READ", "UPDATE", "DELETE"] + } workflow { actions = ["UPDATE", "DELETE"]