diff --git a/CHANGELOG.md b/CHANGELOG.md index c7fde107490..678f060f0d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,17 @@ IMPROVEMENTS: +- *New Resource:* _alicloud_ram_policy_ [GH-46] +- *New Resource*: _alicloud_ram_user_policy_attachment_ [GH-46] - *New Resource* _alicloud_ram_user_ [GH-44] +- *New Datasource* _alicloud_ram_policies_ [GH-46] - *New Datasource* _alicloud_ram_users_ [GH-44] - Added support for importing: + - `alicloud_ram_policy` [GH-46] - `alicloud_ram_user` [GH-44] + ## 0.1.1 (December 11, 2017) IMPROVEMENTS: @@ -48,4 +53,4 @@ BUG FIXES: NOTES: -* Same functionality as that of Terraform 0.9.8. Repacked as part of [Provider Splitout](https://www.hashicorp.com/blog/upcoming-provider-changes-in-terraform-0-10/) +* Same functionality as that of Terraform 0.9.8. Repacked as part of [Provider Splitout](https://www.hashicorp.com/blog/upcoming-provider-changes-in-terraform-0-10/) \ No newline at end of file diff --git a/alicloud/data_source_alicloud_ram_policies.go b/alicloud/data_source_alicloud_ram_policies.go new file mode 100644 index 00000000000..a43593e22cf --- /dev/null +++ b/alicloud/data_source_alicloud_ram_policies.go @@ -0,0 +1,230 @@ +package alicloud + +import ( + "fmt" + "log" + "regexp" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAlicloudRamPolicies() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAlicloudRamPoliciesRead, + + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validatePolicyType, + }, + "name_regex": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "group_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamGroupName, + }, + "user_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamName, + }, + "role_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamName, + }, + "output_file": { + Type: schema.TypeString, + Optional: true, + }, + + // Computed values + "policies": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "default_version": { + Type: schema.TypeString, + Computed: true, + }, + "create_date": { + Type: schema.TypeString, + Computed: true, + }, + "update_date": { + Type: schema.TypeString, + Computed: true, + }, + "attachment_count": { + Type: schema.TypeInt, + Computed: true, + }, + "document": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAlicloudRamPoliciesRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + allPolicies := []interface{}{} + + allPoliciesMap := make(map[string]interface{}) + userFilterPoliciesMap := make(map[string]interface{}) + groupFilterPoliciesMap := make(map[string]interface{}) + roleFilterPoliciesMap := make(map[string]interface{}) + + dataMap := []map[string]interface{}{} + + userName, userNameOk := d.GetOk("user_name") + groupName, groupNameOk := d.GetOk("group_name") + roleName, roleNameOk := d.GetOk("role_name") + policyType, policyTypeOk := d.GetOk("type") + nameRegex, nameRegexOk := d.GetOk("name_regex") + + // policies filtered by name_regex and type + args := ram.PolicyQueryRequest{} + for { + resp, err := conn.ListPolicies(args) + if err != nil { + return fmt.Errorf("ListPolicies got an error: %#v", err) + } + for _, v := range resp.Policies.Policy { + if policyTypeOk && policyType.(string) != v.PolicyType { + continue + } + if nameRegexOk { + r := regexp.MustCompile(nameRegex.(string)) + if !r.MatchString(v.PolicyName) { + continue + } + } + allPoliciesMap[v.PolicyType+v.PolicyName] = v + } + if !resp.IsTruncated { + break + } + args.Marker = resp.Marker + } + + // policies for user + if userNameOk { + resp, err := conn.ListPoliciesForUser(ram.UserQueryRequest{UserName: userName.(string)}) + if err != nil { + return fmt.Errorf("ListPoliciesForUser got an error: %#v", err) + } + + for _, v := range resp.Policies.Policy { + userFilterPoliciesMap[v.PolicyType+v.PolicyName] = v + } + dataMap = append(dataMap, userFilterPoliciesMap) + } + + // policies for group + if groupNameOk { + resp, err := conn.ListPoliciesForGroup(ram.GroupQueryRequest{GroupName: groupName.(string)}) + if err != nil { + return fmt.Errorf("ListPoliciesForGroup got an error: %#v", err) + } + + for _, v := range resp.Policies.Policy { + groupFilterPoliciesMap[v.PolicyType+v.PolicyName] = v + } + dataMap = append(dataMap, groupFilterPoliciesMap) + } + + // policies for role + if roleNameOk { + resp, err := conn.ListPoliciesForRole(ram.RoleQueryRequest{RoleName: roleName.(string)}) + if err != nil { + return fmt.Errorf("ListPoliciesForRole got an error: %#v", err) + } + + for _, v := range resp.Policies.Policy { + roleFilterPoliciesMap[v.PolicyType+v.PolicyName] = v + } + dataMap = append(dataMap, roleFilterPoliciesMap) + } + + // GetIntersection of each map + allPolicies = GetIntersection(dataMap, allPoliciesMap) + + if len(allPolicies) < 1 { + return fmt.Errorf("Your query returned no results. Please change your search criteria and try again.") + } + + log.Printf("[DEBUG] alicloud_ram_policies - Policies found: %#v", allPolicies) + + return ramPoliciesDescriptionAttributes(d, allPolicies, meta) +} + +func ramPoliciesDescriptionAttributes(d *schema.ResourceData, policies []interface{}, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + var ids []string + var s []map[string]interface{} + for _, v := range policies { + policy := v.(ram.Policy) + resp, err := conn.GetPolicyVersionNew(ram.PolicyRequest{ + PolicyName: policy.PolicyName, + PolicyType: ram.Type(policy.PolicyType), + VersionId: policy.DefaultVersion, + }) + if err != nil { + return err + } + + mapping := map[string]interface{}{ + "name": policy.PolicyName, + "type": policy.PolicyType, + "description": policy.Description, + "default_version": policy.DefaultVersion, + "attachment_count": int(policy.AttachmentCount), + "create_date": policy.CreateDate, + "update_date": policy.UpdateDate, + "document": resp.PolicyVersion.PolicyDocument, + } + + log.Printf("[DEBUG] alicloud_ram_policies - adding policy: %v", mapping) + ids = append(ids, policy.PolicyName) + s = append(s, mapping) + } + + d.SetId(dataResourceIdHash(ids)) + if err := d.Set("policies", s); err != nil { + return err + } + + // create a json file in current directory and write data source to it. + if output, ok := d.GetOk("output_file"); ok && output.(string) != "" { + writeToFile(output.(string), s) + } + return nil +} diff --git a/alicloud/data_source_alicloud_ram_policies_test.go b/alicloud/data_source_alicloud_ram_policies_test.go new file mode 100644 index 00000000000..7d97180144d --- /dev/null +++ b/alicloud/data_source_alicloud_ram_policies_test.go @@ -0,0 +1,149 @@ +package alicloud + +import ( + "github.com/hashicorp/terraform/helper/resource" + "testing" +) + +func TestAccAlicloudRamPoliciesDataSource_for_group(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForGroupConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "1"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.policy_name", "ReadOnlyAccess"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.policy_type", "System"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.description", "只读访问所有阿里云资源的权限"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.default_version", "v2"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.0.policy_document", "{\n \"Version\": \"1\", \n \"Statement\": [\n {\n \"Action\": [\n \"*:Describe*\", \n \"*:List*\", \n \"*:Get*\", \n \"*:BatchGet*\", \n \"*:Query*\", \n \"*:BatchQuery*\", \n \"actiontrail:LookupEvents\", \n \"dm:Desc*\", \n \"dm:SenderStatistics*\"\n ], \n \"Resource\": \"*\", \n \"Effect\": \"Allow\"\n }\n ]\n}"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_for_role(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForRoleConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "1"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_for_user(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForUserConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "2"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_for_all(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourceForAllConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "131"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_policy_name_regex(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourcePolicyNameRegexConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "74"), + ), + }, + }, + }) +} + +func TestAccAlicloudRamPoliciesDataSource_policy_type(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudRamPoliciessDataSourcePolicyTypeConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_ram_policies.policy"), + resource.TestCheckResourceAttr("data.alicloud_ram_policies.policy", "policies.#", "1"), + ), + }, + }, + }) +} + +const testAccCheckAlicloudRamPoliciessDataSourceForGroupConfig = ` +data "alicloud_ram_policies" "policy" { + group_name = "group2" +}` + +const testAccCheckAlicloudRamPoliciessDataSourceForRoleConfig = ` +data "alicloud_ram_policies" "policy" { + role_name = "testrole" + type = "Custom" +}` + +const testAccCheckAlicloudRamPoliciessDataSourceForUserConfig = ` +data "alicloud_ram_policies" "policy" { + user_name = "user1" +}` + +const testAccCheckAlicloudRamPoliciessDataSourceForAllConfig = ` +data "alicloud_ram_policies" "policy" { +}` + +const testAccCheckAlicloudRamPoliciessDataSourcePolicyNameRegexConfig = ` +data "alicloud_ram_policies" "policy" { + name_regex = ".*Full.*" +}` + +const testAccCheckAlicloudRamPoliciessDataSourcePolicyTypeConfig = ` +data "alicloud_ram_policies" "policy" { + type = "Custom" +}` diff --git a/alicloud/errors.go b/alicloud/errors.go index 7f40fec8b9a..468a8b5efd4 100644 --- a/alicloud/errors.go +++ b/alicloud/errors.go @@ -23,10 +23,12 @@ const ( InstanceIncorrectStatus = "IncorrectInstanceStatus" HaVipIncorrectStatus = "IncorrectHaVipStatus" // slb - LoadBalancerNotFound = "InvalidLoadBalancerId.NotFound" - UnsupportedProtocalPort = "UnsupportedOperationonfixedprotocalport" - ListenerNotFound = "The specified resource does not exist" - ListenerAlreadyExists = "ListenerAlreadyExists" + LoadBalancerNotFound = "InvalidLoadBalancerId.NotFound" + UnsupportedProtocalPort = "UnsupportedOperationonfixedprotocalport" + ListenerNotFound = "The specified resource does not exist" + ListenerAlreadyExists = "ListenerAlreadyExists" + ServiceIsConfiguring = "ServiceIsConfiguring" + BackendServerconfiguring = "BackendServer.configuring" // security_group InvalidInstanceIdAlreadyExists = "InvalidInstanceId.AlreadyExists" InvalidSecurityGroupIdNotFound = "InvalidSecurityGroupId.NotFound" diff --git a/alicloud/import_alicloud_ram_policy_test.go b/alicloud/import_alicloud_ram_policy_test.go new file mode 100644 index 00000000000..c694a683043 --- /dev/null +++ b/alicloud/import_alicloud_ram_policy_test.go @@ -0,0 +1,29 @@ +package alicloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAlicloudRamPolicy_importBasic(t *testing.T) { + resourceName := "alicloud_ram_policy.policy" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRamPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRamPolicyConfig, + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"force"}, + }, + }, + }) +} diff --git a/alicloud/provider.go b/alicloud/provider.go index a13c560dc35..09d224b1871 100644 --- a/alicloud/provider.go +++ b/alicloud/provider.go @@ -39,7 +39,7 @@ func Provider() terraform.ResourceProvider { "alicloud_instance_types": dataSourceAlicloudInstanceTypes(), "alicloud_vpcs": dataSourceAlicloudVpcs(), "alicloud_key_pairs": dataSourceAlicloudKeyPairs(), - "alicloud_ram_users": dataSourceAlicloudRamUsers(), + "alicloud_ram_policies": dataSourceAlicloudRamPolicies(), }, ResourcesMap: map[string]*schema.Resource{ "alicloud_instance": resourceAliyunInstance(), @@ -55,21 +55,22 @@ func Provider() terraform.ResourceProvider { "alicloud_vpc": resourceAliyunVpc(), "alicloud_nat_gateway": resourceAliyunNatGateway(), //both subnet and vswith exists,cause compatible old version, and compatible aws habit. - "alicloud_subnet": resourceAliyunSubnet(), - "alicloud_vswitch": resourceAliyunSubnet(), - "alicloud_route_entry": resourceAliyunRouteEntry(), - "alicloud_snat_entry": resourceAliyunSnatEntry(), - "alicloud_forward_entry": resourceAliyunForwardEntry(), - "alicloud_eip": resourceAliyunEip(), - "alicloud_eip_association": resourceAliyunEipAssociation(), - "alicloud_slb": resourceAliyunSlb(), - "alicloud_slb_attachment": resourceAliyunSlbAttachment(), - "alicloud_oss_bucket": resourceAlicloudOssBucket(), - "alicloud_oss_bucket_object": resourceAlicloudOssBucketObject(), - "alicloud_key_pair": resourceAlicloudKeyPair(), - "alicloud_key_pair_attachment": resourceAlicloudKeyPairAttachment(), - "alicloud_ram_user": resourceAlicloudRamUser(), - "alicloud_router_interface": resourceAlicloudRouterInterface(), + "alicloud_subnet": resourceAliyunSubnet(), + "alicloud_vswitch": resourceAliyunSubnet(), + "alicloud_route_entry": resourceAliyunRouteEntry(), + "alicloud_snat_entry": resourceAliyunSnatEntry(), + "alicloud_forward_entry": resourceAliyunForwardEntry(), + "alicloud_eip": resourceAliyunEip(), + "alicloud_eip_association": resourceAliyunEipAssociation(), + "alicloud_slb": resourceAliyunSlb(), + "alicloud_slb_attachment": resourceAliyunSlbAttachment(), + "alicloud_oss_bucket": resourceAlicloudOssBucket(), + "alicloud_oss_bucket_object": resourceAlicloudOssBucketObject(), + "alicloud_key_pair": resourceAlicloudKeyPair(), + "alicloud_key_pair_attachment": resourceAlicloudKeyPairAttachment(), + "alicloud_ram_policy": resourceAlicloudRamPolicy(), + "alicloud_ram_user_policy_attachment": resourceAlicloudRamUserPolicyAtatchment(), + "alicloud_router_interface": resourceAlicloudRouterInterface(), }, ConfigureFunc: providerConfigure, diff --git a/alicloud/resource_alicloud_ram_policy.go b/alicloud/resource_alicloud_ram_policy.go new file mode 100644 index 00000000000..c9114dfa52a --- /dev/null +++ b/alicloud/resource_alicloud_ram_policy.go @@ -0,0 +1,329 @@ +package alicloud + +import ( + "fmt" + "time" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAlicloudRamPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceAlicloudRamPolicyCreate, + Read: resourceAlicloudRamPolicyRead, + Update: resourceAlicloudRamPolicyUpdate, + Delete: resourceAlicloudRamPolicyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRamPolicyName, + }, + "statement": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "effect": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := Effect(v.(string)) + if value != Allow && value != Deny { + errors = append(errors, fmt.Errorf( + "%q must be '%s' or '%s'.", k, Allow, Deny)) + } + return + }, + }, + "action": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "resource": &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + ConflictsWith: []string{"document"}, + }, + "document": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"statement", "version"}, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + if len(value) > 2048 { + es = append(es, fmt.Errorf("%q can not be longer than 2048 characters.", k)) + } + return + }, + }, + "description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateRamDesc, + }, + "version": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "1", + ConflictsWith: []string{"document"}, + ValidateFunc: validatePolicyDocVersion, + }, + "force": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "type": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + "attachment_count": &schema.Schema{ + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func resourceAlicloudRamPolicyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args, err := buildAlicloudRamPolicyCreateArgs(d, meta) + if err != nil { + return err + } + + response, err := conn.CreatePolicy(args) + if err != nil { + return fmt.Errorf("CreatePolicy got an error: %#v", err) + } + + d.SetId(response.Policy.PolicyName) + return resourceAlicloudRamPolicyUpdate(d, meta) +} + +func resourceAlicloudRamPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + d.Partial(true) + + args, attributeUpdate, err := buildAlicloudRamPolicyUpdateArgs(d, meta) + if err != nil { + return err + } + + if !d.IsNewResource() && attributeUpdate { + if _, err := conn.CreatePolicyVersion(args); err != nil { + return fmt.Errorf("Error updating policy %s: %#v", d.Id(), err) + } + } + + d.Partial(false) + + return resourceAlicloudRamPolicyRead(d, meta) +} + +func resourceAlicloudRamPolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.PolicyRequest{ + PolicyName: d.Id(), + PolicyType: ram.Custom, + } + + policyResp, err := conn.GetPolicy(args) + if err != nil { + if RamEntityNotExist(err) { + d.SetId("") + } + return fmt.Errorf("GetPolicy got an error: %#v", err) + } + policy := policyResp.Policy + + args.VersionId = policy.DefaultVersion + policyVersionResp, err := conn.GetPolicyVersionNew(args) + if err != nil { + return fmt.Errorf("GetPolicyVersion got an error: %#v", err) + } + + statement, version, err := ParsePolicyDocument(policyVersionResp.PolicyVersion.PolicyDocument) + if err != nil { + return err + } + + d.Set("name", policy.PolicyName) + d.Set("type", policy.PolicyType) + d.Set("description", policy.Description) + d.Set("attachment_count", policy.AttachmentCount) + d.Set("version", version) + d.Set("statement", statement) + d.Set("document", policyVersionResp.PolicyVersion.PolicyDocument) + + return nil +} + +func resourceAlicloudRamPolicyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.PolicyRequest{ + PolicyName: d.Id(), + } + + if d.Get("force").(bool) { + args.PolicyType = ram.Custom + + // list and detach entities for this policy + response, err := conn.ListEntitiesForPolicy(args) + if err != nil { + return fmt.Errorf("Error listing entities for policy %s when trying to delete: %#v", d.Id(), err) + } + + if len(response.Users.User) > 0 { + for _, v := range response.Users.User { + _, err := conn.DetachPolicyFromUser(ram.AttachPolicyRequest{ + PolicyRequest: args, + UserName: v.UserName, + }) + if err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error detaching policy %s from user %s:%#v", d.Id(), v.UserId, err) + } + } + } + + if len(response.Groups.Group) > 0 { + for _, v := range response.Groups.Group { + _, err := conn.DetachPolicyFromGroup(ram.AttachPolicyToGroupRequest{ + PolicyRequest: args, + GroupName: v.GroupName, + }) + if err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error detaching policy %s from group %s:%#v", d.Id(), v.GroupName, err) + } + } + } + + if len(response.Roles.Role) > 0 { + for _, v := range response.Roles.Role { + _, err := conn.DetachPolicyFromRole(ram.AttachPolicyToRoleRequest{ + PolicyRequest: args, + RoleName: v.RoleName, + }) + if err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error detaching policy %s from role %s:%#v", d.Id(), v.RoleId, err) + } + } + } + + // list and delete policy version which are not default + pvResp, err := conn.ListPolicyVersionsNew(args) + if err != nil { + return fmt.Errorf("Error listing policy versions for policy %s:%#v", d.Id(), err) + } + if len(pvResp.PolicyVersions.PolicyVersion) > 1 { + for _, v := range pvResp.PolicyVersions.PolicyVersion { + if !v.IsDefaultVersion { + args.VersionId = v.VersionId + if _, err = conn.DeletePolicyVersion(args); err != nil && !RamEntityNotExist(err) { + return fmt.Errorf("Error delete policy version %s for policy %s:%#v", v.VersionId, d.Id(), err) + } + } + } + } + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + if _, err := conn.DeletePolicy(args); err != nil { + if IsExceptedError(err, DeleteConflictPolicyUser) || IsExceptedError(err, DeleteConflictPolicyGroup) || IsExceptedError(err, DeleteConflictRolePolicy) { + return resource.RetryableError(fmt.Errorf("The policy can not been attached to any user or group or role while deleting the policy. - you can set force with true to force delete the policy.")) + } + if IsExceptedError(err, DeleteConflictPolicyVersion) { + return resource.RetryableError(fmt.Errorf("The policy can not has any version except the defaul version. - you can set force with true to force delete the policy.")) + } + return resource.NonRetryableError(fmt.Errorf("Error deleting policy %s: %#v", d.Id(), err)) + } + return nil + }) +} + +func buildAlicloudRamPolicyCreateArgs(d *schema.ResourceData, meta interface{}) (ram.PolicyRequest, error) { + var document string + + doc, docOk := d.GetOk("document") + statement, statementOk := d.GetOk("statement") + + if !docOk && !statementOk { + return ram.PolicyRequest{}, fmt.Errorf("One of 'document' and 'statement' must be specified.") + } + + if docOk { + document = doc.(string) + } else { + doc, err := AssemblePolicyDocument(statement.(*schema.Set).List(), d.Get("version").(string)) + if err != nil { + return ram.PolicyRequest{}, err + } + document = doc + } + + args := ram.PolicyRequest{ + PolicyName: d.Get("name").(string), + PolicyDocument: document, + } + + if v, ok := d.GetOk("description"); ok && v.(string) != "" { + args.Description = v.(string) + } + + return args, nil +} + +func buildAlicloudRamPolicyUpdateArgs(d *schema.ResourceData, meta interface{}) (ram.PolicyRequest, bool, error) { + args := ram.PolicyRequest{ + PolicyName: d.Id(), + SetAsDefault: "true", + } + + attributeUpdate := false + if d.HasChange("document") { + d.SetPartial("document") + attributeUpdate = true + args.PolicyDocument = d.Get("document").(string) + + } else if d.HasChange("statement") || d.HasChange("version") { + attributeUpdate = true + + if d.HasChange("statement") { + d.SetPartial("statement") + } + if d.HasChange("version") { + d.SetPartial("version") + } + + document, err := AssemblePolicyDocument(d.Get("statement").(*schema.Set).List(), d.Get("version").(string)) + if err != nil { + return ram.PolicyRequest{}, attributeUpdate, err + } + args.PolicyDocument = document + } + + return args, attributeUpdate, nil +} diff --git a/alicloud/resource_alicloud_ram_policy_test.go b/alicloud/resource_alicloud_ram_policy_test.go new file mode 100644 index 00000000000..49e2f26d83e --- /dev/null +++ b/alicloud/resource_alicloud_ram_policy_test.go @@ -0,0 +1,120 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" +) + +func TestAccAlicloudRamPolicy_basic(t *testing.T) { + var v ram.Policy + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_ram_policy.policy", + + Providers: testAccProviders, + CheckDestroy: testAccCheckRamPolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRamPolicyConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRamPolicyExists( + "alicloud_ram_policy.policy", &v), + resource.TestCheckResourceAttr( + "alicloud_ram_policy.policy", + "name", + "policyname"), + resource.TestCheckResourceAttr( + "alicloud_ram_policy.policy", + "description", + "this is a policy test"), + ), + }, + }, + }) + +} + +func testAccCheckRamPolicyExists(n string, policy *ram.Policy) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Policy ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.PolicyRequest{ + PolicyName: rs.Primary.ID, + PolicyType: ram.Custom, + } + + response, err := conn.GetPolicy(request) + log.Printf("[WARN] Policy id %#v", rs.Primary.ID) + + if err == nil { + *policy = response.Policy + return nil + } + return fmt.Errorf("Error finding policy %#v", rs.Primary.ID) + } +} + +func testAccCheckRamPolicyDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_ram_policy" { + continue + } + + // Try to find the policy + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.PolicyRequest{ + PolicyName: rs.Primary.ID, + PolicyType: ram.Custom, + } + + _, err := conn.GetPolicy(request) + + if err != nil { + if RamEntityNotExist(err) { + return nil + } + return err + } + } + return nil +} + +const testAccRamPolicyConfig = ` +resource "alicloud_ram_policy" "policy" { + name = "policyname" + statement = [ + { + effect = "Deny" + action = [ + "oss:ListObjects", + "oss:ListObjects"] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*"] + }] + description = "this is a policy test" + force = true +}` diff --git a/alicloud/resource_alicloud_ram_user_policy_attachment.go b/alicloud/resource_alicloud_ram_user_policy_attachment.go new file mode 100644 index 00000000000..9e63d233259 --- /dev/null +++ b/alicloud/resource_alicloud_ram_user_policy_attachment.go @@ -0,0 +1,119 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "time" +) + +func resourceAlicloudRamUserPolicyAtatchment() *schema.Resource { + return &schema.Resource{ + Create: resourceAlicloudRamUserPolicyAttachmentCreate, + Read: resourceAlicloudRamUserPolicyAttachmentRead, + Delete: resourceAlicloudRamUserPolicyAttachmentDelete, + + Schema: map[string]*schema.Schema{ + "user_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRamName, + }, + "policy_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateRamPolicyName, + }, + "policy_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validatePolicyType, + }, + }, + } +} + +func resourceAlicloudRamUserPolicyAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.AttachPolicyRequest{ + PolicyRequest: ram.PolicyRequest{ + PolicyName: d.Get("policy_name").(string), + PolicyType: ram.Type(d.Get("policy_type").(string)), + }, + UserName: d.Get("user_name").(string), + } + + if _, err := conn.AttachPolicyToUser(args); err != nil { + return fmt.Errorf("AttachPolicyToUser got an error: %#v", err) + } + + d.SetId("user" + args.PolicyName + string(args.PolicyType) + args.UserName) + return resourceAlicloudRamUserPolicyAttachmentRead(d, meta) +} + +func resourceAlicloudRamUserPolicyAttachmentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.UserQueryRequest{ + UserName: d.Get("user_name").(string), + } + + response, err := conn.ListPoliciesForUser(args) + if err != nil { + return fmt.Errorf("Get list policies for user got an error: %#v", err) + } + + if len(response.Policies.Policy) > 0 { + for _, v := range response.Policies.Policy { + if v.PolicyName == d.Get("policy_name").(string) && v.PolicyType == d.Get("policy_type").(string) { + d.Set("user_name", args.UserName) + d.Set("policy_name", v.PolicyName) + d.Set("policy_type", v.PolicyType) + return nil + } + } + } + + d.SetId("") + return nil +} + +func resourceAlicloudRamUserPolicyAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ramconn + + args := ram.AttachPolicyRequest{ + PolicyRequest: ram.PolicyRequest{ + PolicyName: d.Get("policy_name").(string), + PolicyType: ram.Type(d.Get("policy_type").(string)), + }, + UserName: d.Get("user_name").(string), + } + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + if _, err := conn.DetachPolicyFromUser(args); err != nil { + if RamEntityNotExist(err) { + return nil + } + return resource.NonRetryableError(fmt.Errorf("Error deleting user policy attachment: %#v", err)) + } + + response, err := conn.ListPoliciesForUser(ram.UserQueryRequest{UserName: args.UserName}) + if err != nil { + if RamEntityNotExist(err) { + return nil + } + + return resource.NonRetryableError(err) + } + + if len(response.Policies.Policy) < 1 { + return nil + } + return resource.RetryableError(fmt.Errorf("Error deleting user policy attachment - trying again while it is deleted.")) + }) +} diff --git a/alicloud/resource_alicloud_ram_user_policy_attachment_test.go b/alicloud/resource_alicloud_ram_user_policy_attachment_test.go new file mode 100644 index 00000000000..d2212d0f609 --- /dev/null +++ b/alicloud/resource_alicloud_ram_user_policy_attachment_test.go @@ -0,0 +1,140 @@ +package alicloud + +import ( + "fmt" + "testing" + + "github.com/denverdino/aliyungo/ram" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAlicloudRamUserPolicyAttachment_basic(t *testing.T) { + var p ram.Policy + var u ram.User + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_ram_user_policy_attachment.attach", + + Providers: testAccProviders, + CheckDestroy: testAccCheckRamUserPolicyAttachmentDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRamUserPolicyAttachmentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRamPolicyExists( + "alicloud_ram_policy.policy", &p), + testAccCheckRamUserExists( + "alicloud_ram_user.user", &u), + testAccCheckRamUserPolicyAttachmentExists( + "alicloud_ram_user_policy_attachment.attach", &p, &u), + ), + }, + }, + }) + +} + +func testAccCheckRamUserPolicyAttachmentExists(n string, policy *ram.Policy, user *ram.User) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Attachment ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.UserQueryRequest{ + UserName: user.UserName, + } + + response, err := conn.ListPoliciesForUser(request) + if err == nil { + if len(response.Policies.Policy) > 0 { + for _, v := range response.Policies.Policy { + if v.PolicyName == policy.PolicyName && v.PolicyType == policy.PolicyType { + return nil + } + } + } + return fmt.Errorf("Error finding attach %s", rs.Primary.ID) + } + return fmt.Errorf("Error finding attach %s: %#v", rs.Primary.ID, err) + } +} + +func testAccCheckRamUserPolicyAttachmentDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_ram_user_policy_attachment" { + continue + } + + // Try to find the attachment + client := testAccProvider.Meta().(*AliyunClient) + conn := client.ramconn + + request := ram.UserQueryRequest{ + UserName: rs.Primary.Attributes["user_name"], + } + + response, err := conn.ListPoliciesForUser(request) + + if err != nil { + if RamEntityNotExist(err) { + return nil + } + return err + } + + if len(response.Policies.Policy) > 0 { + for _, v := range response.Policies.Policy { + if v.PolicyName == rs.Primary.Attributes["policy_name"] && v.PolicyType == rs.Primary.Attributes["policy_type"] { + return fmt.Errorf("Error attachment still exist.") + } + } + } + } + return nil +} + +const testAccRamUserPolicyAttachmentConfig = ` +resource "alicloud_ram_policy" "policy" { + name = "policyname" + statement = [ + { + effect = "Deny" + action = [ + "oss:ListObjects", + "oss:ListObjects"] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*"] + }] + description = "this is a policy test" + force = true +} + +resource "alicloud_ram_user" "user" { + name = "username" + display_name = "displayname" + mobile = "86-18888888888" + email = "hello.uuu@aaa.com" + comments = "yoyoyo" +} + +resource "alicloud_ram_user_policy_attachment" "attach" { + policy_name = "${alicloud_ram_policy.policy.name}" + user_name = "${alicloud_ram_user.user.name}" + policy_type = "${alicloud_ram_policy.policy.type}" +}` diff --git a/alicloud/resource_alicloud_security_group_rule.go b/alicloud/resource_alicloud_security_group_rule.go index f728b465350..a8b2f32aff1 100644 --- a/alicloud/resource_alicloud_security_group_rule.go +++ b/alicloud/resource_alicloud_security_group_rule.go @@ -5,6 +5,7 @@ import ( "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "strconv" "strings" "time" ) @@ -43,6 +44,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, + Default: GroupRulePolicyAccept, ValidateFunc: validateSecurityRulePolicy, }, @@ -56,6 +58,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource { Type: schema.TypeInt, Optional: true, ForceNew: true, + Default: 1, ValidateFunc: validateSecurityPriority, }, @@ -96,6 +99,8 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac ptl := d.Get("ip_protocol").(string) port := d.Get("port_range").(string) nicType := d.Get("nic_type").(string) + policy := d.Get("policy").(string) + priority := d.Get("priority").(int) if _, ok := d.GetOk("cidr_ip"); !ok { if _, ok := d.GetOk("source_security_group_id"); !ok { @@ -104,14 +109,14 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac } var autherr error - switch GroupRuleDirection(direction) { - case GroupRuleIngress: + switch ecs.Direction(direction) { + case ecs.DirectionIngress: args, err := buildAliyunSecurityIngressArgs(d, meta) if err != nil { return err } autherr = conn.AuthorizeSecurityGroup(args) - case GroupRuleEgress: + case ecs.DirectionEgress: args, err := buildAliyunSecurityEgressArgs(d, meta) if err != nil { return err @@ -133,7 +138,7 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac } else { cidr_ip = d.Get("source_security_group_id").(string) } - d.SetId(sgId + ":" + direction + ":" + ptl + ":" + port + ":" + nicType + ":" + cidr_ip) + d.SetId(sgId + ":" + direction + ":" + ptl + ":" + port + ":" + nicType + ":" + cidr_ip + ":" + policy + ":" + strconv.Itoa(priority)) return resourceAliyunSecurityGroupRuleRead(d, meta) } @@ -141,14 +146,24 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) parts := strings.Split(d.Id(), ":") + policy := parseSecurityRuleId(d, meta, 6) + strPriority := parseSecurityRuleId(d, meta, 7) + var priority int + if policy == "" || strPriority == "" { + policy = d.Get("policy").(string) + priority = d.Get("priority").(int) + d.SetId(d.Id() + ":" + policy + ":" + strconv.Itoa(priority)) + } else { + prior, err := strconv.Atoi(strPriority) + if err != nil { + return fmt.Errorf("SecrityGroupRuleRead parse rule id gets an error: %#v", err) + } + priority = prior + } sgId := parts[0] direction := parts[1] - ip_protocol := parts[2] - port_range := parts[3] - nic_type := parts[4] - cidr_ip := parts[5] - rules, err := client.DescribeSecurityByAttr(sgId, direction, nic_type) + rule, err := client.DescribeSecurityGroupRule(sgId, direction, parts[2], parts[3], parts[4], parts[5], policy, priority) if err != nil { if NotFoundError(err) { d.SetId("") @@ -157,26 +172,6 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error SecurityGroup rule: %#v", err) } - // Filter security group rule according to its attribute - var rule ecs.PermissionType - for _, ru := range rules.Permissions.Permission { - if strings.ToLower(string(ru.IpProtocol)) == ip_protocol && ru.PortRange == port_range { - cidr := ru.SourceCidrIp - if GroupRuleDirection(direction) == GroupRuleIngress && cidr == "" { - cidr = ru.SourceGroupId - } - if GroupRuleDirection(direction) == GroupRuleEgress { - if cidr = ru.DestCidrIp; cidr == "" { - cidr = ru.DestGroupId - } - } - if cidr == cidr_ip { - rule = ru - break - } - } - } - d.Set("type", rule.Direction) d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol))) d.Set("nic_type", rule.NicType) @@ -185,7 +180,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ d.Set("priority", rule.Priority) d.Set("security_group_id", sgId) //support source and desc by type - if GroupRuleDirection(direction) == GroupRuleIngress { + if ecs.Direction(direction) == ecs.DirectionIngress { d.Set("cidr_ip", rule.SourceCidrIp) d.Set("source_security_group_id", rule.SourceGroupId) d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount) @@ -194,7 +189,6 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ d.Set("source_security_group_id", rule.DestGroupId) d.Set("source_group_owner_account", rule.DestGroupOwnerAccount) } - return nil } @@ -202,7 +196,7 @@ func deleteSecurityGroupRule(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) ruleType := d.Get("type").(string) - if GroupRuleDirection(ruleType) == GroupRuleIngress { + if ecs.Direction(ruleType) == ecs.DirectionIngress { args, err := buildAliyunSecurityIngressArgs(d, meta) if err != nil { return err @@ -227,7 +221,20 @@ func deleteSecurityGroupRule(d *schema.ResourceData, meta interface{}) error { func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) parts := strings.Split(d.Id(), ":") - sgId, direction, ip_protocol, port_range, nic_type := parts[0], parts[1], parts[2], parts[3], parts[4] + policy := parseSecurityRuleId(d, meta, 6) + strPriority := parseSecurityRuleId(d, meta, 7) + var priority int + if policy == "" || strPriority == "" { + policy = d.Get("policy").(string) + priority = d.Get("priority").(int) + d.SetId(d.Id() + ":" + policy + ":" + strconv.Itoa(priority)) + } else { + prior, err := strconv.Atoi(strPriority) + if err != nil { + return fmt.Errorf("SecrityGroupRuleRead parse rule id gets an error: %#v", err) + } + priority = prior + } return resource.Retry(5*time.Minute, func() *resource.RetryError { err := deleteSecurityGroupRule(d, meta) @@ -236,7 +243,7 @@ func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interfac resource.RetryableError(fmt.Errorf("Security group rule in use - trying again while it is deleted.")) } - _, err = client.DescribeSecurityGroupRule(sgId, direction, nic_type, ip_protocol, port_range) + _, err = client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], policy, priority) if err != nil { if NotFoundError(err) { return nil @@ -371,3 +378,14 @@ func buildAliyunSecurityEgressArgs(d *schema.ResourceData, meta interface{}) (*e return args, nil } + +func parseSecurityRuleId(d *schema.ResourceData, meta interface{}, index int) (result string) { + parts := strings.Split(d.Id(), ":") + defer func() { + if e := recover(); e != nil { + fmt.Printf("Panicing %s\r\n", e) + result = "" + } + }() + return parts[index] +} diff --git a/alicloud/resource_alicloud_security_group_rule_test.go b/alicloud/resource_alicloud_security_group_rule_test.go index 2b3f965e3bf..231086d0262 100644 --- a/alicloud/resource_alicloud_security_group_rule_test.go +++ b/alicloud/resource_alicloud_security_group_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/terraform" "log" "regexp" + "strconv" "strings" "testing" ) @@ -270,6 +271,45 @@ func TestAccAlicloudSecurityGroupRule_Multi(t *testing.T) { } +func TestAccAlicloudSecurityGroupRule_MultiAttri(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.ingress_allow_tcp_22_sg", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleMultiAttri, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_allow_tcp_22_sg", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress_allow_tcp_22_sg", "port_range", "22/22"), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_allow_tcp_22", &pt), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_deny_tcp_22", &pt), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_allow_tcp_22_prior", &pt), + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress_deny_tcp_22_prior", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress_deny_tcp_22_prior", + "port_range", + "22/22"), + ), + }, + }, + }) + +} + func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -284,8 +324,11 @@ func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resour client := testAccProvider.Meta().(*AliyunClient) log.Printf("[WARN]get sg rule %s", rs.Primary.ID) parts := strings.Split(rs.Primary.ID, ":") - // securityGroupId, direction, nicType, ipProtocol, portRange - rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3]) + prior, err := strconv.Atoi(parts[7]) + if err != nil { + return fmt.Errorf("testSecrityGroupRuleExists parse rule id gets an error: %#v", err) + } + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], prior) if err != nil { return err @@ -309,7 +352,11 @@ func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error { } parts := strings.Split(rs.Primary.ID, ":") - rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3]) + prior, err := strconv.Atoi(parts[7]) + if err != nil { + return fmt.Errorf("testSecrityGroupRuleDestroy parse rule id gets an error: %#v", err) + } + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], prior) if rule != nil { return fmt.Errorf("Error SecurityGroup Rule still exist") @@ -497,3 +544,83 @@ resource "alicloud_security_group_rule" "ingress" { source_security_group_id = "${alicloud_security_group.foo.id}" } ` +const testAccSecurityGroupRuleMultiAttri = ` +variable "name" { + default = "test_ssh" +} + +variable "source_cidr_blocks" { + type = "list" + default = ["0.0.0.0/0"] +} + + +resource "alicloud_vpc" "vpc" { + name = "test-ssh" + cidr_block = "172.16.0.0/24" +} + +resource "alicloud_security_group" "main" { + name = "${var.name}" + vpc_id = "${alicloud_vpc.vpc.id}" +} +resource "alicloud_security_group" "source" { + name = "${var.name}-2" + vpc_id = "${alicloud_vpc.vpc.id}" +} + +resource "alicloud_security_group_rule" "ingress_allow_tcp_22_sg" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.main.id}" + source_security_group_id = "${alicloud_security_group.source.id}" +} + +resource "alicloud_security_group_rule" "ingress_allow_tcp_22" { + count = "${length(var.source_cidr_blocks)}" + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "${element(var.source_cidr_blocks, count.index)}" +} + +resource "alicloud_security_group_rule" "ingress_deny_tcp_22" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "drop" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ingress_allow_tcp_22_prior" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 100 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "0.0.0.0/0" +} +resource "alicloud_security_group_rule" "ingress_deny_tcp_22_prior" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "drop" + port_range = "22/22" + priority = 100 + security_group_id = "${alicloud_security_group.main.id}" + cidr_ip = "0.0.0.0/0" +} +` diff --git a/alicloud/service_alicloud_ecs.go b/alicloud/service_alicloud_ecs.go index 8ee0bb0bfca..850bf3a6bc8 100644 --- a/alicloud/service_alicloud_ecs.go +++ b/alicloud/service_alicloud_ecs.go @@ -6,6 +6,7 @@ import ( "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/schema" + "log" "strings" ) @@ -222,29 +223,36 @@ func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.Descr return client.ecsconn.DescribeSecurityGroupAttribute(args) } -func (client *AliyunClient) DescribeSecurityByAttr(securityGroupId, direction, nicType string) (*ecs.DescribeSecurityGroupAttributeResponse, error) { - - args := &ecs.DescribeSecurityGroupAttributeArgs{ +func (client *AliyunClient) DescribeSecurityGroupRule(groupId, direction, ipProtocol, portRange, nicType, cidr_ip, policy string, priority int) (*ecs.PermissionType, error) { + rules, err := client.ecsconn.DescribeSecurityGroupAttribute(&ecs.DescribeSecurityGroupAttributeArgs{ RegionId: client.Region, - SecurityGroupId: securityGroupId, - Direction: direction, + SecurityGroupId: groupId, + Direction: ecs.Direction(direction), NicType: ecs.NicType(nicType), - } - - return client.ecsconn.DescribeSecurityGroupAttribute(args) -} + }) -func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, direction, nicType, ipProtocol, portRange string) (*ecs.PermissionType, error) { - sg, err := client.DescribeSecurityByAttr(securityGroupId, direction, nicType) if err != nil { return nil, err } - for _, p := range sg.Permissions.Permission { - if strings.ToLower(string(p.IpProtocol)) == ipProtocol && p.PortRange == portRange { - return &p, nil + for _, ru := range rules.Permissions.Permission { + if strings.ToLower(string(ru.IpProtocol)) == ipProtocol && ru.PortRange == portRange { + cidr := ru.SourceCidrIp + if ecs.Direction(direction) == ecs.DirectionIngress && cidr == "" { + cidr = ru.SourceGroupId + } + if ecs.Direction(direction) == ecs.DirectionEgress { + if cidr = ru.DestCidrIp; cidr == "" { + cidr = ru.DestGroupId + } + } + + if cidr == cidr_ip && strings.ToLower(string(ru.Policy)) == policy && ru.Priority == priority { + return &ru, nil + } } } + return nil, GetNotFoundErrorFromString("Security group rule not found") } @@ -295,14 +303,70 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta // Retrieve series instance type family ioOptimized := ecs.IoOptimizedOptimized var expectedFamilies []string - outdatedFamiliesMap, expectedFamiliesMap, err := client.FetchSpecifiedInstanceTypeFamily(getRegion(d, meta), zoneId, []string{GenerationOne, GenerationTwo}, zones) + mapOutdatedInstanceFamilies, mapUpgradedInstanceFamilies, err := client.FetchSpecifiedInstanceTypeFamily(getRegion(d, meta), zoneId, []string{GenerationOne, GenerationTwo}, zones) if err != nil { return nil, err } - for key := range expectedFamiliesMap { + for key := range mapUpgradedInstanceFamilies { expectedFamilies = append(expectedFamilies, key) } + validData := make(map[ResourceKeyType]interface{}) + mapZones := make(map[string]ecs.ZoneType) + mapSupportedInstanceTypes := make(map[string]string) + mapUpgradedInstanceTypes := make(map[string]string) + mapOutdatedInstanceTypes := make(map[string]string) + mapOutdatedDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) + mapDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) + for _, zone := range zones { + //Filter and get all instance types in the zones + for _, insType := range zone.AvailableInstanceTypes.InstanceTypes { + if _, ok := mapSupportedInstanceTypes[insType]; !ok { + insTypeSplit := strings.Split(insType, DOT_SEPARATED) + mapSupportedInstanceTypes[insType] = string(insTypeSplit[0] + DOT_SEPARATED + insTypeSplit[1]) + } + } + if len(zone.AvailableDiskCategories.DiskCategories) < 1 { + continue + } + //Filter and get all instance types in the zones + for _, category := range zone.AvailableDiskCategories.DiskCategories { + if _, ok := SupportedDiskCategory[category]; ok { + mapDiskCategories[category] = category + } + if _, ok := OutdatedDiskCategory[category]; ok { + mapOutdatedDiskCategories[category] = category + } + } + resources := zone.AvailableResources.ResourcesInfo + if len(resources) < 1 { + continue + } + mapZones[zone.ZoneId] = zone + } + //separate all instance types according generation 3 in the zones + for key, _ := range mapSupportedInstanceTypes { + find := false + for out, _ := range mapOutdatedInstanceFamilies { + if strings.HasPrefix(key, out) { + mapOutdatedInstanceTypes[key] = out + mapSupportedInstanceTypes[key] = out + find = true + break + } + } + if find { + continue + } + for upgrade, _ := range mapUpgradedInstanceFamilies { + if strings.HasPrefix(key, upgrade) { + mapUpgradedInstanceTypes[key] = upgrade + mapSupportedInstanceTypes[key] = upgrade + break + } + } + } + var instanceType string if insType, ok := d.GetOk("available_instance_type"); ok { instanceType = insType.(string) @@ -310,35 +374,36 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta instanceType = insType.(string) } if instanceType != "" { - - instanceTypeSplit := strings.Split(instanceType, DOT_SEPARATED) - prefix := string(instanceTypeSplit[0] + DOT_SEPARATED + instanceTypeSplit[1]) + if !strings.HasPrefix(instanceType, "ecs.") { + return nil, fmt.Errorf("Invalid instance_type: %s. Please modify it and try again.", instanceType) + } var instanceTypeObject ecs.InstanceTypeItemType - _, outdatedOk := outdatedFamiliesMap[prefix] - _, expectedOk := expectedFamiliesMap[prefix] - if outdatedOk || expectedOk { - mapInstanceTypes, err := client.FetchSpecifiedInstanceTypesByFamily(zoneId, prefix, zones) + targetFamily, ok := mapSupportedInstanceTypes[instanceType] + if ok { + mapInstanceTypes, err := client.FetchSpecifiedInstanceTypesByFamily(zoneId, targetFamily, zones) if err != nil { return nil, err } - if value, ok := mapInstanceTypes[instanceType]; ok { - instanceTypeObject = value - } + var validInstanceTypes []string for key, value := range mapInstanceTypes { if instanceType == key { instanceTypeObject = mapInstanceTypes[key] break } - validInstanceTypes = append(validInstanceTypes, fmt.Sprintf("%s(%dCPU,%.0fGB)", key, value.CpuCoreCount, value.MemorySize)) + core := "Core" + if value.CpuCoreCount > 1 { + core = "Cores" + } + validInstanceTypes = append(validInstanceTypes, fmt.Sprintf("%s(%d%s,%.0fGB)", key, value.CpuCoreCount, core, value.MemorySize)) } if instanceTypeObject.InstanceTypeId == "" { if zoneId == "" { return nil, fmt.Errorf("Instance type %s is not supported in the region %s. Expected instance types of family %s: %s.", - instanceType, getRegion(d, meta), prefix, strings.Join(validInstanceTypes, ", ")) + instanceType, getRegion(d, meta), targetFamily, strings.Join(validInstanceTypes, ", ")) } return nil, fmt.Errorf("Instance type %s is not supported in the availability zone %s. Expected instance types of family %s: %s.", - instanceType, zoneId, prefix, strings.Join(validInstanceTypes, ", ")) + instanceType, zoneId, targetFamily, strings.Join(validInstanceTypes, ", ")) } outDisk := false @@ -346,6 +411,9 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta _, outDisk = OutdatedDiskCategory[ecs.DiskCategory(disk.(string))] } + _, outdatedOk := mapOutdatedInstanceTypes[instanceType] + _, expectedOk := mapUpgradedInstanceTypes[instanceType] + if expectedOk && outDisk { return nil, fmt.Errorf("Instance type %s can't support 'cloud' as instance system disk. "+ "Please change your disk category to efficient disk '%s' or '%s'.", instanceType, ecs.DiskCategoryCloudSSD, ecs.DiskCategoryCloudEfficiency) @@ -353,16 +421,20 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta if outdatedOk { var expectedEqualCpus []string var expectedEqualMoreCpus []string - for _, fam := range expectedFamilies { + for _, fam := range mapUpgradedInstanceTypes { mapInstanceTypes, err := client.FetchSpecifiedInstanceTypesByFamily(zoneId, fam, zones) if err != nil { return nil, err } for _, value := range mapInstanceTypes { + core := "Core" + if value.CpuCoreCount > 1 { + core = "Cores" + } if instanceTypeObject.CpuCoreCount == value.CpuCoreCount { - expectedEqualCpus = append(expectedEqualCpus, fmt.Sprintf("%s(%dCPU,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, value.MemorySize)) + expectedEqualCpus = append(expectedEqualCpus, fmt.Sprintf("%s(%d%s,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, core, value.MemorySize)) } else if instanceTypeObject.CpuCoreCount*2 == value.CpuCoreCount { - expectedEqualMoreCpus = append(expectedEqualMoreCpus, fmt.Sprintf("%s(%dCPU,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, value.MemorySize)) + expectedEqualMoreCpus = append(expectedEqualMoreCpus, fmt.Sprintf("%s(%d%s,%.0fGB)", value.InstanceTypeId, value.CpuCoreCount, core, value.MemorySize)) } } } @@ -372,13 +444,17 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta } if out, ok := d.GetOk("is_outdated"); !(ok && out.(bool)) { - return nil, fmt.Errorf("The current instance type %s(%dCPU,%.0fGB) has been outdated. Expect to use the upgraded instance types: %s. You can keep the instance type %s by setting 'is_outdated' to true.", - instanceType, instanceTypeObject.CpuCoreCount, instanceTypeObject.MemorySize, strings.Join(expectedInstanceTypes, ", "), instanceType) + core := "Core" + if instanceTypeObject.CpuCoreCount > 1 { + core = "Cores" + } + return nil, fmt.Errorf("The current instance type %s(%d%s,%.0fGB) has been outdated. Expect to use the upgraded instance types: %s. You can keep the instance type %s by setting 'is_outdated' to true.", + instanceType, instanceTypeObject.CpuCoreCount, core, instanceTypeObject.MemorySize, strings.Join(expectedInstanceTypes, ", "), instanceType) } else { // Check none io optimized and cloud _, typeOk := NoneIoOptimizedInstanceType[instanceType] - _, famOk := NoneIoOptimizedFamily[prefix] - _, halfOk := HalfIoOptimizedFamily[prefix] + _, famOk := NoneIoOptimizedFamily[targetFamily] + _, halfOk := HalfIoOptimizedFamily[targetFamily] if typeOk || famOk { if outDisk { ioOptimized = ecs.IoOptimizedNone @@ -397,93 +473,32 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta } } - } else { - var validFamilies []string - for key := range expectedFamiliesMap { - validFamilies = append(validFamilies, key) - } - - if len(validFamilies) < 1 { - return nil, fmt.Errorf("There is no available instance type in the current availability zone." + - "Please change availability zone or region and try again.") - } - if zoneId == "" { - return nil, fmt.Errorf("Instance type family %s is not supported in the region %s. Expected instance type families: %s.", - prefix, getRegion(d, meta), strings.Join(validFamilies, ", ")) - } - return nil, fmt.Errorf("Instance type family %s is not supported in the availability zone %s. Expected instance type families: %s.", - prefix, zoneId, strings.Join(validFamilies, ", ")) + } else if err := getExpectInstanceTypesAndFormatOut(zoneId, targetFamily, getRegion(d, meta), mapUpgradedInstanceFamilies); err != nil { + return nil, err } } if instanceTypeFamily, ok := d.GetOk("instance_type_family"); ok { - _, outdatedOk := outdatedFamiliesMap[instanceTypeFamily.(string)] - _, expectedOk := expectedFamiliesMap[instanceTypeFamily.(string)] - if outdatedOk || !expectedOk { - var validFamilies []string - for key := range expectedFamiliesMap { - validFamilies = append(validFamilies, key) - } - if len(validFamilies) < 1 { - return nil, fmt.Errorf("There is no available instance type family in the current availability zone." + - "Please change availability zone or region and try again.") - } - if zoneId == "" { - return nil, fmt.Errorf("Instance type family %s is not supported in the region %s. Expected instance type families: %s.", - instanceTypeFamily, getRegion(d, meta), strings.Join(validFamilies, ", ")) - } - return nil, fmt.Errorf("Instance type family %s is not supported in the availability zone %s. Expected instance type families: %s.", - instanceTypeFamily, zoneId, strings.Join(validFamilies, ", ")) + if !strings.HasPrefix(instanceTypeFamily.(string), "ecs.") { + return nil, fmt.Errorf("Invalid instance_type_family: %s. Please modify it and try again.", instanceTypeFamily.(string)) } - } - validData := make(map[ResourceKeyType]interface{}) - mapZones := make(map[string]ecs.ZoneType) - mapSupportedInstanceTypes := make(map[string]string) - mapUpgradedInstanceTypes := make(map[string]string) - mapOutdatedInstanceTypes := make(map[string]string) - mapOutdatedDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) - mapDiskCategories := make(map[ecs.DiskCategory]ecs.DiskCategory) - for _, zone := range zones { - //var validInstanceTypes []string - for _, insType := range zone.AvailableInstanceTypes.InstanceTypes { - insTypeSplit := strings.Split(insType, DOT_SEPARATED) - prefix := string(insTypeSplit[0] + DOT_SEPARATED + insTypeSplit[1]) - if _, ok := outdatedFamiliesMap[prefix]; ok { - mapOutdatedInstanceTypes[insType] = prefix - } - if _, ok := expectedFamiliesMap[prefix]; ok { - mapUpgradedInstanceTypes[insType] = prefix - } - mapSupportedInstanceTypes[insType] = prefix - } - if len(zone.AvailableDiskCategories.DiskCategories) < 1 { - continue - } - //var validDiskCategories []ecs.DiskCategory - for _, category := range zone.AvailableDiskCategories.DiskCategories { - if _, ok := SupportedDiskCategory[category]; ok { - mapDiskCategories[category] = category - } - if _, ok := OutdatedDiskCategory[category]; ok { - mapOutdatedDiskCategories[category] = category + _, outdatedOk := mapOutdatedInstanceFamilies[instanceTypeFamily.(string)] + _, expectedOk := mapUpgradedInstanceFamilies[instanceTypeFamily.(string)] + if outdatedOk || !expectedOk { + if err := getExpectInstanceTypesAndFormatOut(zoneId, instanceTypeFamily.(string), getRegion(d, meta), mapUpgradedInstanceFamilies); err != nil { + return nil, err } } - resources := zone.AvailableResources.ResourcesInfo - if len(resources) < 1 { - continue - } - mapZones[zone.ZoneId] = zone - } validData[ZoneKey] = mapZones validData[InstanceTypeKey] = mapSupportedInstanceTypes validData[UpgradedInstanceTypeKey] = mapUpgradedInstanceTypes validData[OutdatedInstanceTypeKey] = mapOutdatedInstanceTypes - validData[UpgradedInstanceTypeFamilyKey] = expectedFamiliesMap - validData[OutdatedInstanceTypeFamilyKey] = outdatedFamiliesMap + validData[UpgradedInstanceTypeFamilyKey] = mapUpgradedInstanceFamilies + validData[OutdatedInstanceTypeFamilyKey] = mapOutdatedInstanceFamilies validData[OutdatedDiskCategoryKey] = mapOutdatedDiskCategories validData[DiskCategoryKey] = mapDiskCategories validData[IoOptimizedKey] = ioOptimized @@ -493,14 +508,16 @@ func (client *AliyunClient) CheckParameterValidity(d *schema.ResourceData, meta func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Region, zoneId string, generations []string, all_zones []ecs.ZoneType) (map[string]ecs.InstanceTypeFamily, map[string]ecs.InstanceTypeFamily, error) { // Describe specified series instance type families - outdatedFamiliesMap := make(map[string]ecs.InstanceTypeFamily) - upgradedFamiliesMap := make(map[string]ecs.InstanceTypeFamily) + mapOutdatedInstanceFamilies := make(map[string]ecs.InstanceTypeFamily) + mapUpgradedInstanceFamilies := make(map[string]ecs.InstanceTypeFamily) response, err := client.ecsconn.DescribeInstanceTypeFamilies(&ecs.DescribeInstanceTypeFamiliesArgs{ RegionId: regionId, }) if err != nil { return nil, nil, fmt.Errorf("Error DescribeInstanceTypeFamilies: %#v.", err) } + log.Printf("All the instance families in the region %s: %#v", regionId, response) + tempOutdatedMap := make(map[string]string) for _, gen := range generations { tempOutdatedMap[gen] = gen @@ -508,10 +525,10 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Reg for _, family := range response.InstanceTypeFamilies.InstanceTypeFamily { if _, ok := tempOutdatedMap[family.Generation]; ok { - outdatedFamiliesMap[family.InstanceTypeFamilyId] = family + mapOutdatedInstanceFamilies[family.InstanceTypeFamilyId] = family continue } - upgradedFamiliesMap[family.InstanceTypeFamilyId] = family + mapUpgradedInstanceFamilies[family.InstanceTypeFamilyId] = family } // Filter specified zone's instance type families, and make them fit for specified generation @@ -523,10 +540,10 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Reg for _, resource := range zone.AvailableResources.ResourcesInfo { families := resource.InstanceTypeFamilies[ecs.SupportedInstanceTypeFamily] for _, familyId := range families { - if val, ok := outdatedFamiliesMap[familyId]; ok { + if val, ok := mapOutdatedInstanceFamilies[familyId]; ok { outdatedValidFamilies[familyId] = val } - if val, ok := upgradedFamiliesMap[familyId]; ok { + if val, ok := mapUpgradedInstanceFamilies[familyId]; ok { upgradedValidFamilies[familyId] = val } } @@ -536,7 +553,9 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypeFamily(regionId common.Reg } } } - return outdatedFamiliesMap, upgradedFamiliesMap, nil + log.Printf("New generation instance families: %#v.\n Outdated instance families: %#v.", + mapUpgradedInstanceFamilies, mapOutdatedInstanceFamilies) + return mapOutdatedInstanceFamilies, mapUpgradedInstanceFamilies, nil } func (client *AliyunClient) FetchSpecifiedInstanceTypesByFamily(zoneId, instanceTypeFamily string, all_zones []ecs.ZoneType) (map[string]ecs.InstanceTypeItemType, error) { @@ -547,6 +566,7 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypesByFamily(zoneId, instance if err != nil { return nil, fmt.Errorf("Error DescribeInstanceTypes: %#v.", err) } + log.Printf("All the instance types of family %s: %#v", instanceTypeFamily, types) instanceTypes := make(map[string]ecs.InstanceTypeItemType) for _, ty := range types { instanceTypes[ty.InstanceTypeId] = ty @@ -573,6 +593,24 @@ func (client *AliyunClient) FetchSpecifiedInstanceTypesByFamily(zoneId, instance return instanceTypes, nil } +func getExpectInstanceTypesAndFormatOut(zoneId, instanceTypeFamily string, regionId common.Region, mapInstanceFamilies map[string]ecs.InstanceTypeFamily) error { + var validFamilies []string + + for key := range mapInstanceFamilies { + validFamilies = append(validFamilies, key) + } + if len(validFamilies) < 1 { + return fmt.Errorf("There is no available instance type family in the current availability zone." + + "Please change availability zone or region and try again.") + } + if zoneId == "" { + return fmt.Errorf("Instance type family %s is not supported in the region %s. Expected instance type families: %s.", + instanceTypeFamily, regionId, strings.Join(validFamilies, ", ")) + } + return fmt.Errorf("Instance type family %s is not supported in the availability zone %s. Expected instance type families: %s.", + instanceTypeFamily, zoneId, strings.Join(validFamilies, ", ")) +} + func (client *AliyunClient) QueryInstancesWithKeyPair(region common.Region, instanceIds, keypair string) ([]interface{}, []ecs.InstanceAttributesType, error) { var instance_ids []interface{} var instanceList []ecs.InstanceAttributesType diff --git a/alicloud/service_alicloud_rds.go b/alicloud/service_alicloud_rds.go index 931f1cb4518..b7b7d412053 100644 --- a/alicloud/service_alicloud_rds.go +++ b/alicloud/service_alicloud_rds.go @@ -141,13 +141,9 @@ func (client *AliyunClient) ConfigDBBackup(instanceId, backupTime, backupPeriod } func (client *AliyunClient) ModifyDBSecurityIps(instanceId, ips string) error { - sargs := rds.DBInstanceIPArray{ - SecurityIps: ips, - } - args := rds.ModifySecurityIpsArgs{ - DBInstanceId: instanceId, - DBInstanceIPArray: sargs, + DBInstanceId: instanceId, + SecurityIps: ips, } if _, err := client.rdsconn.ModifySecurityIps(&args); err != nil { diff --git a/alicloud/validators.go b/alicloud/validators.go index 36e4580c734..913459ee823 100644 --- a/alicloud/validators.go +++ b/alicloud/validators.go @@ -129,9 +129,9 @@ func validateSecurityGroupDescription(v interface{}, k string) (ws []string, err } func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) { - rt := GroupRuleDirection(v.(string)) - if rt != GroupRuleIngress && rt != GroupRuleEgress { - errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleIngress, GroupRuleEgress)) + rt := ecs.Direction(v.(string)) + if rt != ecs.DirectionIngress && rt != ecs.DirectionEgress { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, ecs.DirectionIngress, ecs.DirectionEgress)) } return diff --git a/vendor/github.com/denverdino/aliyungo/common/regions.go b/vendor/github.com/denverdino/aliyungo/common/regions.go index c87fe0691a0..199ee5915d3 100644 --- a/vendor/github.com/denverdino/aliyungo/common/regions.go +++ b/vendor/github.com/denverdino/aliyungo/common/regions.go @@ -24,6 +24,9 @@ const ( MEEast1 = Region("me-east-1") EUCentral1 = Region("eu-central-1") + + ShenZhenFinance = Region("cn-shenzhen-finance-1") + ShanghaiFinance = Region("cn-shanghai-finance-1") ) var ValidRegions = []Region{ @@ -32,4 +35,5 @@ var ValidRegions = []Region{ APNorthEast1, APSouthEast1, APSouthEast2, APSouthEast3, MEEast1, EUCentral1, + ShenZhenFinance, ShanghaiFinance, } diff --git a/vendor/github.com/denverdino/aliyungo/cs/signature.go b/vendor/github.com/denverdino/aliyungo/cs/signature.go index aa18d43785c..e8d94b5a60c 100644 --- a/vendor/github.com/denverdino/aliyungo/cs/signature.go +++ b/vendor/github.com/denverdino/aliyungo/cs/signature.go @@ -36,7 +36,6 @@ func canonicalizeHeader(headers http.Header) (newHeaders http.Header, result str newHeaders = http.Header{} for k, v := range headers { - fmt.Printf("Header[%d] = %++v", k, v) if lower := strings.ToLower(k); strings.HasPrefix(lower, headerOSSPrefix) { newHeaders[lower] = v canonicalizedHeaders = append(canonicalizedHeaders, lower) diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instances.go b/vendor/github.com/denverdino/aliyungo/ecs/instances.go index 8c9da7f7641..74be6e50130 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/instances.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/instances.go @@ -440,6 +440,28 @@ func (client *Client) DescribeInstancesWithRaw(args *DescribeInstancesArgs) (res return response, nil } +type ModifyInstanceAutoReleaseTimeArgs struct { + InstanceId string + AutoReleaseTime string +} + +type ModifyInstanceAutoReleaseTimeResponse struct { + common.Response +} + +// 对给定的实例设定自动释放时间。 +// +// You can read doc at https://help.aliyun.com/document_detail/47576.html +func (client *Client) ModifyInstanceAutoReleaseTime(instanceId, time string) error { + args := ModifyInstanceAutoReleaseTimeArgs{ + InstanceId: instanceId, + AutoReleaseTime: time, + } + response := ModifyInstanceAutoReleaseTimeResponse{} + err := client.Invoke("ModifyInstanceAutoReleaseTime", &args, &response) + return err +} + type DeleteInstanceArgs struct { InstanceId string } diff --git a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go index 54af3a79824..a35de940756 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go @@ -6,10 +6,15 @@ import ( ) type NicType string +type Direction string const ( NicTypeInternet = NicType("internet") NicTypeIntranet = NicType("intranet") + + DirectionIngress = Direction("ingress") + DirectionEgress = Direction("egress") + DirectionAll = Direction("all") ) type IpProtocol string @@ -32,8 +37,8 @@ const ( type DescribeSecurityGroupAttributeArgs struct { SecurityGroupId string RegionId common.Region - NicType NicType //enum for internet (default) |intranet - Direction string // enum ingress egress + NicType NicType //enum for internet (default) |intranet + Direction Direction // enum ingress egress } // diff --git a/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go index f3c1b09c55e..0b7cbe87bf0 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/snapshots.go @@ -26,6 +26,8 @@ type SnapshotType struct { SourceDiskSize int SourceDiskType string //enum for System | Data ProductCode string + Status string + Usage string CreationTime util.ISO6801Time } diff --git a/vendor/github.com/denverdino/aliyungo/rds/instances.go b/vendor/github.com/denverdino/aliyungo/rds/instances.go index f73e4e58e3b..55b13ba57de 100644 --- a/vendor/github.com/denverdino/aliyungo/rds/instances.go +++ b/vendor/github.com/denverdino/aliyungo/rds/instances.go @@ -14,8 +14,10 @@ type DBInstanceIPArray struct { // ref: https://help.aliyun.com/document_detail/26242.html type ModifySecurityIpsArgs struct { - DBInstanceId string - DBInstanceIPArray + DBInstanceId string + SecurityIps string + DBInstanceIPArrayName string + DBInstanceIPArrayAttribute string } func (client *Client) ModifySecurityIps(args *ModifySecurityIpsArgs) (resp common.Response, err error) { @@ -229,7 +231,6 @@ type DBInstanceAttribute struct { VpcId string } - type ReadOnlyDBInstanceIds struct { ReadOnlyDBInstanceId []ReadOnlyDBInstanceId } diff --git a/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go index d090570cee4..e30fdb53497 100644 --- a/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go +++ b/vendor/github.com/denverdino/aliyungo/slb/loadbalancers.go @@ -27,6 +27,8 @@ type CreateLoadBalancerArgs struct { InternetChargeType InternetChargeType Bandwidth int ClientToken string + MasterZoneId string + SlaveZoneId string } type CreateLoadBalancerResponse struct { diff --git a/vendor/vendor.json b/vendor/vendor.json index b0c352fe20f..82c6b6c9f48 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -235,68 +235,68 @@ { "checksumSHA1": "9JK6TQ6tSG68roB1KWs3NIxw6zY=", "path": "github.com/denverdino/aliyungo/cdn", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "m7wNx+GFEKUDPxYgp3Je1ptrQHg=", + "checksumSHA1": "qvFutFkayxTxJN3E1AENo4kHEzA=", "path": "github.com/denverdino/aliyungo/common", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "Bx3JJdE4DJLDly0sLrzTTrLY2KE=", + "checksumSHA1": "iINyIl3tdnJ1149OjjDIYM8o7gk=", "path": "github.com/denverdino/aliyungo/cs", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "zHBzMaiaG1TbhrU15Wp/7Rbl4ZY=", "path": "github.com/denverdino/aliyungo/dns", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "YkdiutqXn8zDDDCbwiOuscPTZtQ=", + "checksumSHA1": "dRCnl3Jccqs69IUO8s90ZUez/Vc=", "path": "github.com/denverdino/aliyungo/ecs", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "0qcm0qCFWDyeYjG+y596rgwEcQ4=", "path": "github.com/denverdino/aliyungo/ess", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "CPuR3s7LzQkT57Z20zMHXQM2MYQ=", "path": "github.com/denverdino/aliyungo/location", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "sievsBvgtVF2iZ2FjmDZppH3+Ro=", "path": "github.com/denverdino/aliyungo/ram", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "EaiPS3gTXG0g4DZGeTB07SARwr0=", + "checksumSHA1": "5muc4YGlXvIK6BwWNXQppIEcEkg=", "path": "github.com/denverdino/aliyungo/rds", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { - "checksumSHA1": "pQHH9wpyS0e4wpW0erxe3D7OILM=", + "checksumSHA1": "lCoboKg3pzrdnhtP+mcu6F48+UY=", "path": "github.com/denverdino/aliyungo/slb", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "cKVBRn7GKT+0IqfGUc/NnKDWzCw=", "path": "github.com/denverdino/aliyungo/util", - "revision": "bf3ea483bd236ba1b7bdd60027d5be8375207533", - "revisionTime": "2017-12-06T09:23:29Z" + "revision": "d7c7e038ca2b9ffd3c2b5525d9eae4a31b0704c7", + "revisionTime": "2017-12-07T06:26:40Z" }, { "checksumSHA1": "BCv50o5pDkoSG3vYKOSai1Z8p3w=", diff --git a/website/alicloud.erb b/website/alicloud.erb index 2f4c6118bb7..fa5e9a32d7c 100644 --- a/website/alicloud.erb +++ b/website/alicloud.erb @@ -31,6 +31,9 @@ > alicloud_vpcs + > + alicloud_ram_policies + > alicloud_ram_users @@ -153,9 +156,15 @@ > RAM Resources diff --git a/website/docs/d/ram_policies.html.markdown b/website/docs/d/ram_policies.html.markdown new file mode 100644 index 00000000000..5552262857c --- /dev/null +++ b/website/docs/d/ram_policies.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_ram_policies" +sidebar_current: "docs-alicloud-datasource-ram-policies" +description: |- + Provides a list of ram policies available to the user. +--- + +# alicloud\_ram\_policies + +The Ram Policies data source provides a list of Alicloud Ram Policies in an Alicloud account according to the specified filters. + +## Example Usage + +``` +data "alicloud_ram_policies" "policy" { + output_file = "policies.txt" + user_name = "user1" + group_name = "group1" + type = "System" +} + +``` + +## Argument Reference + +The following arguments are supported: + +* `name_regex` - (Optional) A regex string to apply to the policy list returned by Alicloud. +* `type` - (Optional) Limit search to specific the policy type. Valid items are `Custom` and `System`. +* `user_name` - (Optional) Limit search to specific the user name. Found the policies which attached with the specified user. +* `group_name` - (Optional) Limit search to specific the group name. Found the policies which attached with the specified group. +* `role_name` - (Optional) Limit search to specific the role name. Found the policies which attached with the specified role. +* `output_file` - (Optional) The name of file that can save policies data source after running `terraform plan`. + +## Attributes Reference + +A list of policies will be exported and its every element contains the following attributes: + +* `name` - Name of the policy. +* `type` - Type of the policy. +* `description` - Description of the policy. +* `default_version` - Default version of the policy. +* `create_date` - Create date of the policy. +* `update_date` - Update date of the policy. +* `attachment_count` - Attachment count of the policy. +* `document` - Policy document of the policy. \ No newline at end of file diff --git a/website/docs/r/ram_policy.html.markdown b/website/docs/r/ram_policy.html.markdown new file mode 100644 index 00000000000..877206b6a41 --- /dev/null +++ b/website/docs/r/ram_policy.html.markdown @@ -0,0 +1,63 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_ram_policy" +sidebar_current: "docs-alicloud-resource-ram-policy" +description: |- + Provides a RAM Policy resource. +--- + +# alicloud\_ram\_policy + +Provides a RAM Policy resource. + +~> **NOTE:** When you want to destroy this resource forcefully(means remove all the relationships associated with it automatically and then destroy it) without set `force` with `true` at beginning, you need add `force = true` to configuration file and run `terraform plan`, then you can delete resource forcefully. + +## Example Usage + +``` +# Create a new RAM Policy. +resource "alicloud_ram_policy" "policy" { + name = "test_policy" + statement = [ + { + effect = "Allow" + action = [ + "oss:ListObjects", + "oss:GetObject" + ] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*" + ] + } + ] + description = "this is a policy test" + force = true +} +``` +## Argument Reference + +The following arguments are supported: + +* `name` - (Required, Forces new resource) Name of the RAM policy. This name can have a string of 1 to 128 characters, must contain only alphanumeric characters or hyphen "-", and must not begin with a hyphen. +* `statement` - (Optional, Type: list, Conflicts with `document`) Statements of the RAM policy document. It is required when the `document` is not specified. + * `resource` - (Required, Type: list) List of specific objects which will be authorized. The format of each item in this list is `acs:${service}:${region}:${account_id}:${relative_id}`, such as `acs:ecs:*:*:instance/inst-002` and `acs:oss:*:1234567890000:mybucket`. The `${service}` can be `ecs`, `oss`, `ots` and so on, the `${region}` is the region info which can use `*` replace when it is not supplied, the `${account_id}` refers to someone's Alicloud account id or you can use `*` to replace, the `${relative_id}` is the resource description section which related to the `${service}`. + * `action` - (Required, Type: list) List of operations for the `resource`. The format of each item in this list is `${service}:${action_name}`, such as `oss:ListBuckets` and `ecs:Describe*`. The `${service}` can be `ecs`, `oss`, `ots` and so on, the `${action_name}` refers to the name of an api interface which related to the `${service}`. + * `effect` - (Required) This parameter indicates whether or not the `action` is allowed. Valid values are `Allow` and `Deny`. +* `version` - (Optional, Conflicts with `document`) Version of the RAM policy document. Valid value is `1`. Default value is `1`. +* `document` - (Optional, Conflicts with `statement` and `version`) Document of the RAM policy. It is required when the `statement` is not specified. +* `description` - (Optional, Forces new resource) Description of the RAM policy. This name can have a string of 1 to 1024 characters. +* `force` - (Optional) This parameter is used for resource destroy. Default value is `false`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The policy ID. +* `name` - The policy name. +* `type` - The policy type. +* `description` - The policy description. +* `statement` - List of statement of the policy document. +* `document` - The policy document. +* `version` - The policy document version. +* `attachment_count` - The policy attachment count. \ No newline at end of file diff --git a/website/docs/r/ram_user_policy_attachment.html.markdown b/website/docs/r/ram_user_policy_attachment.html.markdown new file mode 100644 index 00000000000..8fcf492e2ca --- /dev/null +++ b/website/docs/r/ram_user_policy_attachment.html.markdown @@ -0,0 +1,63 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_ram_user_policy_attachment" +sidebar_current: "docs-alicloud-resource-ram-user-policy-attachment" +description: |- + Provides a RAM User Policy attachment resource. +--- + +# alicloud\_ram\_user\_policy\_attachment + +Provides a RAM User Policy attachment resource. + +## Example Usage + +``` +# Create a RAM User Policy attachment. +resource "alicloud_ram_user" "user" { + name = "user_test" + display_name = "user_display_name" + mobile = "86-18688888888" + email = "hello.uuu@aaa.com" + comments = "yoyoyo" + force = true +} + +resource "alicloud_ram_policy" "policy" { + name = "test_policy" + statement = [ + { + effect = "Allow" + action = [ + "oss:ListObjects", + "oss:GetObject"] + resource = [ + "acs:oss:*:*:mybucket", + "acs:oss:*:*:mybucket/*"] + }] + description = "this is a policy test" + force = true +} + +resource "alicloud_ram_user_policy_attachment" "attach" { + policy_name = "${alicloud_ram_policy.policy.name}" + policy_type = "${alicloud_ram_policy.policy.type}" + user_name = "${alicloud_ram_user.user.name}" +} +``` +## Argument Reference + +The following arguments are supported: + +* `user_name` - (Required, Forces new resource) Name of the RAM user. This name can have a string of 1 to 64 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin with a hyphen. +* `policy_name` - (Required, Forces new resource) Name of the RAM policy. This name can have a string of 1 to 128 characters, must contain only alphanumeric characters or hyphen "-", and must not begin with a hyphen. +* `policy_type` - (Required, Forces new resource) Type of the RAM policy. It must be `Custom` or `System`. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The attachment ID. +* `user_name` - The user name. +* `policy_name` - The policy name. +* `policy_type` - The policy type. \ No newline at end of file