github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_iam_policy.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "net/url" 6 "regexp" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/iam" 11 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsIamPolicy() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsIamPolicyCreate, 19 Read: resourceAwsIamPolicyRead, 20 Update: resourceAwsIamPolicyUpdate, 21 Delete: resourceAwsIamPolicyDelete, 22 Importer: &schema.ResourceImporter{ 23 State: schema.ImportStatePassthrough, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "description": &schema.Schema{ 28 Type: schema.TypeString, 29 ForceNew: true, 30 Optional: true, 31 }, 32 "path": &schema.Schema{ 33 Type: schema.TypeString, 34 Optional: true, 35 Default: "/", 36 ForceNew: true, 37 }, 38 "policy": &schema.Schema{ 39 Type: schema.TypeString, 40 Required: true, 41 ValidateFunc: validateJsonString, 42 DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs, 43 }, 44 "name": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 Computed: true, 48 ForceNew: true, 49 ConflictsWith: []string{"name_prefix"}, 50 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 51 // https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334 52 value := v.(string) 53 if len(value) > 128 { 54 errors = append(errors, fmt.Errorf( 55 "%q cannot be longer than 128 characters", k)) 56 } 57 if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) { 58 errors = append(errors, fmt.Errorf( 59 "%q must match [\\w+=,.@-]", k)) 60 } 61 return 62 }, 63 }, 64 "name_prefix": { 65 Type: schema.TypeString, 66 Optional: true, 67 ForceNew: true, 68 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 69 // https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8329-L8334 70 value := v.(string) 71 if len(value) > 96 { 72 errors = append(errors, fmt.Errorf( 73 "%q cannot be longer than 96 characters, name is limited to 128", k)) 74 } 75 if !regexp.MustCompile("^[\\w+=,.@-]*$").MatchString(value) { 76 errors = append(errors, fmt.Errorf( 77 "%q must match [\\w+=,.@-]", k)) 78 } 79 return 80 }, 81 }, 82 "arn": &schema.Schema{ 83 Type: schema.TypeString, 84 Computed: true, 85 }, 86 }, 87 } 88 } 89 90 func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error { 91 iamconn := meta.(*AWSClient).iamconn 92 93 var name string 94 if v, ok := d.GetOk("name"); ok { 95 name = v.(string) 96 } else if v, ok := d.GetOk("name_prefix"); ok { 97 name = resource.PrefixedUniqueId(v.(string)) 98 } else { 99 name = resource.UniqueId() 100 } 101 102 request := &iam.CreatePolicyInput{ 103 Description: aws.String(d.Get("description").(string)), 104 Path: aws.String(d.Get("path").(string)), 105 PolicyDocument: aws.String(d.Get("policy").(string)), 106 PolicyName: aws.String(name), 107 } 108 109 response, err := iamconn.CreatePolicy(request) 110 if err != nil { 111 return fmt.Errorf("Error creating IAM policy %s: %s", name, err) 112 } 113 114 return readIamPolicy(d, response.Policy) 115 } 116 117 func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error { 118 iamconn := meta.(*AWSClient).iamconn 119 120 getPolicyRequest := &iam.GetPolicyInput{ 121 PolicyArn: aws.String(d.Id()), 122 } 123 124 getPolicyResponse, err := iamconn.GetPolicy(getPolicyRequest) 125 if err != nil { 126 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 127 d.SetId("") 128 return nil 129 } 130 return fmt.Errorf("Error reading IAM policy %s: %s", d.Id(), err) 131 } 132 133 getPolicyVersionRequest := &iam.GetPolicyVersionInput{ 134 PolicyArn: aws.String(d.Id()), 135 VersionId: getPolicyResponse.Policy.DefaultVersionId, 136 } 137 138 getPolicyVersionResponse, err := iamconn.GetPolicyVersion(getPolicyVersionRequest) 139 if err != nil { 140 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 141 d.SetId("") 142 return nil 143 } 144 return fmt.Errorf("Error reading IAM policy version %s: %s", d.Id(), err) 145 } 146 147 policy, err := url.QueryUnescape(*getPolicyVersionResponse.PolicyVersion.Document) 148 if err != nil { 149 return err 150 } 151 if err := d.Set("policy", policy); err != nil { 152 return err 153 } 154 155 return readIamPolicy(d, getPolicyResponse.Policy) 156 } 157 158 func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error { 159 iamconn := meta.(*AWSClient).iamconn 160 161 if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil { 162 return err 163 } 164 165 if !d.HasChange("policy") { 166 return nil 167 } 168 request := &iam.CreatePolicyVersionInput{ 169 PolicyArn: aws.String(d.Id()), 170 PolicyDocument: aws.String(d.Get("policy").(string)), 171 SetAsDefault: aws.Bool(true), 172 } 173 174 if _, err := iamconn.CreatePolicyVersion(request); err != nil { 175 return fmt.Errorf("Error updating IAM policy %s: %s", d.Id(), err) 176 } 177 return nil 178 } 179 180 func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error { 181 iamconn := meta.(*AWSClient).iamconn 182 183 if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil { 184 return err 185 } 186 187 request := &iam.DeletePolicyInput{ 188 PolicyArn: aws.String(d.Id()), 189 } 190 191 _, err := iamconn.DeletePolicy(request) 192 if err != nil { 193 if iamerr, ok := err.(awserr.Error); ok && iamerr.Code() == "NoSuchEntity" { 194 return nil 195 } 196 return fmt.Errorf("Error reading IAM policy %s: %#v", d.Id(), err) 197 } 198 return nil 199 } 200 201 // iamPolicyPruneVersions deletes the oldest versions. 202 // 203 // Old versions are deleted until there are 4 or less remaining, which means at 204 // least one more can be created before hitting the maximum of 5. 205 // 206 // The default version is never deleted. 207 208 func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error { 209 versions, err := iamPolicyListVersions(arn, iamconn) 210 if err != nil { 211 return err 212 } 213 if len(versions) < 5 { 214 return nil 215 } 216 217 var oldestVersion *iam.PolicyVersion 218 219 for _, version := range versions { 220 if *version.IsDefaultVersion { 221 continue 222 } 223 if oldestVersion == nil || 224 version.CreateDate.Before(*oldestVersion.CreateDate) { 225 oldestVersion = version 226 } 227 } 228 229 if err := iamPolicyDeleteVersion(arn, *oldestVersion.VersionId, iamconn); err != nil { 230 return err 231 } 232 return nil 233 } 234 235 func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error { 236 versions, err := iamPolicyListVersions(arn, iamconn) 237 if err != nil { 238 return err 239 } 240 241 for _, version := range versions { 242 if *version.IsDefaultVersion { 243 continue 244 } 245 if err := iamPolicyDeleteVersion(arn, *version.VersionId, iamconn); err != nil { 246 return err 247 } 248 } 249 250 return nil 251 } 252 253 func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error { 254 request := &iam.DeletePolicyVersionInput{ 255 PolicyArn: aws.String(arn), 256 VersionId: aws.String(versionID), 257 } 258 259 _, err := iamconn.DeletePolicyVersion(request) 260 if err != nil { 261 return fmt.Errorf("Error deleting version %s from IAM policy %s: %s", versionID, arn, err) 262 } 263 return nil 264 } 265 266 func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) { 267 request := &iam.ListPolicyVersionsInput{ 268 PolicyArn: aws.String(arn), 269 } 270 271 response, err := iamconn.ListPolicyVersions(request) 272 if err != nil { 273 return nil, fmt.Errorf("Error listing versions for IAM policy %s: %s", arn, err) 274 } 275 return response.Versions, nil 276 } 277 278 func readIamPolicy(d *schema.ResourceData, policy *iam.Policy) error { 279 d.SetId(*policy.Arn) 280 if policy.Description != nil { 281 // the description isn't present in the response to CreatePolicy. 282 if err := d.Set("description", policy.Description); err != nil { 283 return err 284 } 285 } 286 if err := d.Set("path", policy.Path); err != nil { 287 return err 288 } 289 if err := d.Set("name", policy.PolicyName); err != nil { 290 return err 291 } 292 if err := d.Set("arn", policy.Arn); err != nil { 293 return err 294 } 295 return nil 296 }