/vendor/github.com/terraform-providers/terraform-provider-aws/aws/resource_aws_iam_policy.go

https://github.com/openshift/installer · Go · 316 lines · 256 code · 51 blank · 9 comment · 57 complexity · 8de61d5fc5f331a9814888fa9a8b5fbf MD5 · raw file

  1. package aws
  2. import (
  3. "fmt"
  4. "log"
  5. "net/url"
  6. "regexp"
  7. "time"
  8. "github.com/aws/aws-sdk-go/aws"
  9. "github.com/aws/aws-sdk-go/service/iam"
  10. "github.com/hashicorp/terraform-plugin-sdk/helper/resource"
  11. "github.com/hashicorp/terraform-plugin-sdk/helper/schema"
  12. "github.com/hashicorp/terraform-plugin-sdk/helper/validation"
  13. )
  14. func resourceAwsIamPolicy() *schema.Resource {
  15. return &schema.Resource{
  16. Create: resourceAwsIamPolicyCreate,
  17. Read: resourceAwsIamPolicyRead,
  18. Update: resourceAwsIamPolicyUpdate,
  19. Delete: resourceAwsIamPolicyDelete,
  20. Importer: &schema.ResourceImporter{
  21. State: schema.ImportStatePassthrough,
  22. },
  23. Schema: map[string]*schema.Schema{
  24. "description": {
  25. Type: schema.TypeString,
  26. ForceNew: true,
  27. Optional: true,
  28. },
  29. "path": {
  30. Type: schema.TypeString,
  31. Optional: true,
  32. Default: "/",
  33. ForceNew: true,
  34. },
  35. "policy": {
  36. Type: schema.TypeString,
  37. Required: true,
  38. ValidateFunc: validateIAMPolicyJson,
  39. DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
  40. },
  41. "name": {
  42. Type: schema.TypeString,
  43. Optional: true,
  44. Computed: true,
  45. ForceNew: true,
  46. ConflictsWith: []string{"name_prefix"},
  47. ValidateFunc: validation.All(
  48. validation.StringLenBetween(1, 128),
  49. validation.StringMatch(regexp.MustCompile(`^[\w+=,.@-]*$`), "must match [\\w+=,.@-]"),
  50. ),
  51. },
  52. "name_prefix": {
  53. Type: schema.TypeString,
  54. Optional: true,
  55. ForceNew: true,
  56. ConflictsWith: []string{"name"},
  57. ValidateFunc: validation.All(
  58. validation.StringLenBetween(1, 96),
  59. validation.StringMatch(regexp.MustCompile(`^[\w+=,.@-]*$`), "must match [\\w+=,.@-]"),
  60. ),
  61. },
  62. "arn": {
  63. Type: schema.TypeString,
  64. Computed: true,
  65. },
  66. },
  67. }
  68. }
  69. func resourceAwsIamPolicyCreate(d *schema.ResourceData, meta interface{}) error {
  70. iamconn := meta.(*AWSClient).iamconn
  71. var name string
  72. if v, ok := d.GetOk("name"); ok {
  73. name = v.(string)
  74. } else if v, ok := d.GetOk("name_prefix"); ok {
  75. name = resource.PrefixedUniqueId(v.(string))
  76. } else {
  77. name = resource.UniqueId()
  78. }
  79. request := &iam.CreatePolicyInput{
  80. Description: aws.String(d.Get("description").(string)),
  81. Path: aws.String(d.Get("path").(string)),
  82. PolicyDocument: aws.String(d.Get("policy").(string)),
  83. PolicyName: aws.String(name),
  84. }
  85. response, err := iamconn.CreatePolicy(request)
  86. if err != nil {
  87. return fmt.Errorf("Error creating IAM policy %s: %s", name, err)
  88. }
  89. d.SetId(*response.Policy.Arn)
  90. return resourceAwsIamPolicyRead(d, meta)
  91. }
  92. func resourceAwsIamPolicyRead(d *schema.ResourceData, meta interface{}) error {
  93. iamconn := meta.(*AWSClient).iamconn
  94. getPolicyRequest := &iam.GetPolicyInput{
  95. PolicyArn: aws.String(d.Id()),
  96. }
  97. log.Printf("[DEBUG] Getting IAM Policy: %s", getPolicyRequest)
  98. // Handle IAM eventual consistency
  99. var getPolicyResponse *iam.GetPolicyOutput
  100. err := resource.Retry(1*time.Minute, func() *resource.RetryError {
  101. var err error
  102. getPolicyResponse, err = iamconn.GetPolicy(getPolicyRequest)
  103. if d.IsNewResource() && isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
  104. return resource.RetryableError(err)
  105. }
  106. if err != nil {
  107. return resource.NonRetryableError(err)
  108. }
  109. return nil
  110. })
  111. if isResourceTimeoutError(err) {
  112. getPolicyResponse, err = iamconn.GetPolicy(getPolicyRequest)
  113. }
  114. if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
  115. log.Printf("[WARN] IAM Policy (%s) not found, removing from state", d.Id())
  116. d.SetId("")
  117. return nil
  118. }
  119. if err != nil {
  120. return fmt.Errorf("Error reading IAM policy %s: %s", d.Id(), err)
  121. }
  122. if getPolicyResponse == nil || getPolicyResponse.Policy == nil {
  123. log.Printf("[WARN] IAM Policy (%s) not found, removing from state", d.Id())
  124. d.SetId("")
  125. return nil
  126. }
  127. d.Set("arn", getPolicyResponse.Policy.Arn)
  128. d.Set("description", getPolicyResponse.Policy.Description)
  129. d.Set("name", getPolicyResponse.Policy.PolicyName)
  130. d.Set("path", getPolicyResponse.Policy.Path)
  131. // Retrieve policy
  132. getPolicyVersionRequest := &iam.GetPolicyVersionInput{
  133. PolicyArn: aws.String(d.Id()),
  134. VersionId: getPolicyResponse.Policy.DefaultVersionId,
  135. }
  136. log.Printf("[DEBUG] Getting IAM Policy Version: %s", getPolicyVersionRequest)
  137. // Handle IAM eventual consistency
  138. var getPolicyVersionResponse *iam.GetPolicyVersionOutput
  139. err = resource.Retry(1*time.Minute, func() *resource.RetryError {
  140. var err error
  141. getPolicyVersionResponse, err = iamconn.GetPolicyVersion(getPolicyVersionRequest)
  142. if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
  143. return resource.RetryableError(err)
  144. }
  145. if err != nil {
  146. return resource.NonRetryableError(err)
  147. }
  148. return nil
  149. })
  150. if isResourceTimeoutError(err) {
  151. getPolicyVersionResponse, err = iamconn.GetPolicyVersion(getPolicyVersionRequest)
  152. }
  153. if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
  154. log.Printf("[WARN] IAM Policy (%s) not found, removing from state", d.Id())
  155. d.SetId("")
  156. return nil
  157. }
  158. if err != nil {
  159. return fmt.Errorf("Error reading IAM policy version %s: %s", d.Id(), err)
  160. }
  161. policy := ""
  162. if getPolicyVersionResponse != nil && getPolicyVersionResponse.PolicyVersion != nil {
  163. var err error
  164. policy, err = url.QueryUnescape(aws.StringValue(getPolicyVersionResponse.PolicyVersion.Document))
  165. if err != nil {
  166. return fmt.Errorf("error parsing policy: %s", err)
  167. }
  168. }
  169. d.Set("policy", policy)
  170. return nil
  171. }
  172. func resourceAwsIamPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
  173. iamconn := meta.(*AWSClient).iamconn
  174. if err := iamPolicyPruneVersions(d.Id(), iamconn); err != nil {
  175. return err
  176. }
  177. request := &iam.CreatePolicyVersionInput{
  178. PolicyArn: aws.String(d.Id()),
  179. PolicyDocument: aws.String(d.Get("policy").(string)),
  180. SetAsDefault: aws.Bool(true),
  181. }
  182. if _, err := iamconn.CreatePolicyVersion(request); err != nil {
  183. return fmt.Errorf("Error updating IAM policy %s: %s", d.Id(), err)
  184. }
  185. return resourceAwsIamPolicyRead(d, meta)
  186. }
  187. func resourceAwsIamPolicyDelete(d *schema.ResourceData, meta interface{}) error {
  188. iamconn := meta.(*AWSClient).iamconn
  189. if err := iamPolicyDeleteNondefaultVersions(d.Id(), iamconn); err != nil {
  190. return err
  191. }
  192. request := &iam.DeletePolicyInput{
  193. PolicyArn: aws.String(d.Id()),
  194. }
  195. if _, err := iamconn.DeletePolicy(request); err != nil {
  196. if isAWSErr(err, iam.ErrCodeNoSuchEntityException, "") {
  197. return nil
  198. }
  199. return fmt.Errorf("Error deleting IAM policy %s: %s", d.Id(), err)
  200. }
  201. return nil
  202. }
  203. // iamPolicyPruneVersions deletes the oldest versions.
  204. //
  205. // Old versions are deleted until there are 4 or less remaining, which means at
  206. // least one more can be created before hitting the maximum of 5.
  207. //
  208. // The default version is never deleted.
  209. func iamPolicyPruneVersions(arn string, iamconn *iam.IAM) error {
  210. versions, err := iamPolicyListVersions(arn, iamconn)
  211. if err != nil {
  212. return err
  213. }
  214. if len(versions) < 5 {
  215. return nil
  216. }
  217. var oldestVersion *iam.PolicyVersion
  218. for _, version := range versions {
  219. if *version.IsDefaultVersion {
  220. continue
  221. }
  222. if oldestVersion == nil ||
  223. version.CreateDate.Before(*oldestVersion.CreateDate) {
  224. oldestVersion = version
  225. }
  226. }
  227. err1 := iamPolicyDeleteVersion(arn, *oldestVersion.VersionId, iamconn)
  228. return err1
  229. }
  230. func iamPolicyDeleteNondefaultVersions(arn string, iamconn *iam.IAM) error {
  231. versions, err := iamPolicyListVersions(arn, iamconn)
  232. if err != nil {
  233. return err
  234. }
  235. for _, version := range versions {
  236. if *version.IsDefaultVersion {
  237. continue
  238. }
  239. if err := iamPolicyDeleteVersion(arn, *version.VersionId, iamconn); err != nil {
  240. return err
  241. }
  242. }
  243. return nil
  244. }
  245. func iamPolicyDeleteVersion(arn, versionID string, iamconn *iam.IAM) error {
  246. request := &iam.DeletePolicyVersionInput{
  247. PolicyArn: aws.String(arn),
  248. VersionId: aws.String(versionID),
  249. }
  250. _, err := iamconn.DeletePolicyVersion(request)
  251. if err != nil {
  252. return fmt.Errorf("Error deleting version %s from IAM policy %s: %s", versionID, arn, err)
  253. }
  254. return nil
  255. }
  256. func iamPolicyListVersions(arn string, iamconn *iam.IAM) ([]*iam.PolicyVersion, error) {
  257. request := &iam.ListPolicyVersionsInput{
  258. PolicyArn: aws.String(arn),
  259. }
  260. response, err := iamconn.ListPolicyVersions(request)
  261. if err != nil {
  262. return nil, fmt.Errorf("Error listing versions for IAM policy %s: %s", arn, err)
  263. }
  264. return response.Versions, nil
  265. }