Handle managed_policy_arns in aws_iam_roles
parent
4ac335e7a1
commit
8dcb5da73d
|
@ -97,6 +97,7 @@ func (d DriftCTL) Run() (*analyser.Analysis, error) {
|
|||
middlewares.NewAwsSqsQueuePolicyExpander(d.resourceFactory, d.resourceSchemaRepository),
|
||||
middlewares.NewAwsDefaultSqsQueuePolicy(),
|
||||
middlewares.NewAwsSNSTopicPolicyExpander(d.resourceFactory, d.resourceSchemaRepository),
|
||||
middlewares.NewAwsRoleManagedPolicyExpander(d.resourceFactory),
|
||||
)
|
||||
|
||||
if !d.strictMode {
|
||||
|
|
|
@ -79,9 +79,10 @@ func runTest(t *testing.T, cases TestCases) {
|
|||
remoteSupplier := &resource.MockSupplier{}
|
||||
remoteSupplier.On("Resources").Return(c.remoteResources, nil)
|
||||
|
||||
resourceFactory := &terraform.MockResourceFactory{}
|
||||
var resourceFactory resource.ResourceFactory = terraform.NewTerraformResourceFactory(repo)
|
||||
|
||||
if c.mocks != nil {
|
||||
resourceFactory = &terraform.MockResourceFactory{}
|
||||
c.mocks(resourceFactory, repo)
|
||||
}
|
||||
|
||||
|
@ -1544,6 +1545,73 @@ func TestDriftctlRun_Middlewares(t *testing.T) {
|
|||
return &pkg.ScanOptions{Filter: f}
|
||||
}(t),
|
||||
},
|
||||
{
|
||||
name: "test aws role managed policy expander",
|
||||
stateResources: []resource.Resource{
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_managed_policy_attr",
|
||||
Type: aws.AwsIamRoleResourceType,
|
||||
Attrs: &resource.Attributes{
|
||||
"name": "role_with_managed_policy_attr",
|
||||
"managed_policy_arns": []interface{}{
|
||||
"arn1",
|
||||
"arn2",
|
||||
},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_empty_managed_policy_attribute",
|
||||
Type: aws.AwsIamRoleResourceType,
|
||||
Attrs: &resource.Attributes{
|
||||
"managed_policy_arns": []interface{}{},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_without_managed_policy_attribute",
|
||||
Type: aws.AwsIamRoleResourceType,
|
||||
Attrs: &resource.Attributes{},
|
||||
},
|
||||
},
|
||||
remoteResources: []resource.Resource{
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_managed_policy_attr",
|
||||
Type: aws.AwsIamRoleResourceType,
|
||||
Attrs: &resource.Attributes{
|
||||
"name": "role_with_managed_policy_attr",
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_managed_policy_attr-arn1",
|
||||
Type: aws.AwsIamPolicyAttachmentResourceType,
|
||||
Attrs: &resource.Attributes{
|
||||
"policy_arn": "arn1",
|
||||
"roles": []interface{}{"role_with_managed_policy_attr"},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_managed_policy_attr-arn2",
|
||||
Type: aws.AwsIamPolicyAttachmentResourceType,
|
||||
Attrs: &resource.Attributes{
|
||||
"policy_arn": "arn2",
|
||||
"roles": []interface{}{"role_with_managed_policy_attr"},
|
||||
},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_with_empty_managed_policy_attribute",
|
||||
Type: aws.AwsIamRoleResourceType,
|
||||
Attrs: &resource.Attributes{},
|
||||
},
|
||||
&resource.AbstractResource{
|
||||
Id: "role_without_managed_policy_attribute",
|
||||
Type: aws.AwsIamRoleResourceType,
|
||||
Attrs: &resource.Attributes{},
|
||||
},
|
||||
},
|
||||
assert: func(result *test.ScanResult, err error) {
|
||||
result.AssertInfrastructureIsInSync()
|
||||
result.AssertManagedCount(5)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runTest(t, cases)
|
||||
|
|
|
@ -28,7 +28,12 @@ func (m AwsDefaults) awsIamRoleDefaults(remoteResources []resource.Resource) []r
|
|||
continue
|
||||
}
|
||||
|
||||
if match := strings.HasPrefix((*remoteResource.(*resource.AbstractResource).Attrs)["path"].(string), defaultIamRolePathPrefix); match {
|
||||
path := remoteResource.Attributes().GetString("path")
|
||||
if path == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if match := strings.HasPrefix(*path, defaultIamRolePathPrefix); match {
|
||||
resourcesToIgnore = append(resourcesToIgnore, remoteResource)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource/aws"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// The role of this middleware is to expand policy contained in `managed_policy_arns` to dedicated `aws_iam_policy_attachment`
|
||||
// resources. Note that we do not use `aws_iam_role_policy_attachment` or `aws_iam_user_policy_attachment`
|
||||
// Once theses resources created, we remove the old `managed_policy_arns` field to avoid false positive drifts
|
||||
|
||||
type AwsRoleManagedPolicyExpander struct {
|
||||
resourceFactory resource.ResourceFactory
|
||||
}
|
||||
|
||||
func NewAwsRoleManagedPolicyExpander(resourceFactory resource.ResourceFactory) *AwsRoleManagedPolicyExpander {
|
||||
return &AwsRoleManagedPolicyExpander{resourceFactory: resourceFactory}
|
||||
}
|
||||
|
||||
func (a AwsRoleManagedPolicyExpander) Execute(remoteResources, resourcesFromState *[]resource.Resource) error {
|
||||
|
||||
newList := make([]resource.Resource, 0)
|
||||
for _, res := range *remoteResources {
|
||||
// Ignore all resources other than iam_role
|
||||
if res.TerraformType() != aws.AwsIamRoleResourceType {
|
||||
newList = append(newList, res)
|
||||
continue
|
||||
}
|
||||
|
||||
res.Attributes().SafeDelete([]string{"managed_policy_arns"})
|
||||
newList = append(newList, res)
|
||||
}
|
||||
*remoteResources = newList
|
||||
|
||||
newList = make([]resource.Resource, 0)
|
||||
for _, res := range *resourcesFromState {
|
||||
// Ignore all resources other than iam_role
|
||||
if res.TerraformType() != aws.AwsIamRoleResourceType {
|
||||
newList = append(newList, res)
|
||||
continue
|
||||
}
|
||||
managedPolicyArns := res.Attributes().GetSlice("managed_policy_arns")
|
||||
|
||||
// if managed_policy_arns does not exist or is empty ignore resource
|
||||
if managedPolicyArns == nil {
|
||||
newList = append(newList, res)
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove empty slices to match remote read results
|
||||
if len(managedPolicyArns) == 0 {
|
||||
res.Attributes().SafeDelete([]string{"managed_policy_arns"})
|
||||
newList = append(newList, res)
|
||||
continue
|
||||
}
|
||||
|
||||
roleName := res.Attributes().GetString("name")
|
||||
|
||||
for _, arn := range managedPolicyArns {
|
||||
arn := arn.(string)
|
||||
policyAttachmentData := resource.Attributes{
|
||||
"policy_arn": arn,
|
||||
"users": []interface{}{},
|
||||
"groups": []interface{}{},
|
||||
"roles": []interface{}{*roleName},
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"role": *roleName,
|
||||
"policy_arn": arn,
|
||||
}).Debug("Expanded managed_policy_arns from role")
|
||||
|
||||
newList = append(newList, a.resourceFactory.CreateAbstractResource(aws.AwsIamPolicyAttachmentResourceType, fmt.Sprintf("%s-%s", *roleName, arn), policyAttachmentData))
|
||||
}
|
||||
|
||||
res.Attributes().SafeDelete([]string{"managed_policy_arns"})
|
||||
|
||||
newList = append(newList, res)
|
||||
|
||||
}
|
||||
*resourcesFromState = newList
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package aws_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/cloudskiff/driftctl/test"
|
||||
"github.com/cloudskiff/driftctl/test/acceptance"
|
||||
)
|
||||
|
||||
func TestAcc_Aws_IamRole(t *testing.T) {
|
||||
acceptance.Run(t, acceptance.AccTestCase{
|
||||
TerraformVersion: "0.14.9",
|
||||
Paths: []string{"./testdata/acc/aws_iam_role"},
|
||||
Args: []string{"scan", "--filter", "Type=='aws_iam_role'"},
|
||||
Checks: []acceptance.AccCheck{
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "us-east-1",
|
||||
},
|
||||
Check: func(result *test.ScanResult, stdout string, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result.AssertDriftCountTotal(0)
|
||||
result.AssertDeletedCount(0)
|
||||
result.AssertManagedCount(1)
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAcc_Aws_IamRole_WithManaged(t *testing.T) {
|
||||
acceptance.Run(t, acceptance.AccTestCase{
|
||||
TerraformVersion: "0.14.9",
|
||||
Paths: []string{"./testdata/acc/aws_iam_role_with_managed_policies"},
|
||||
Args: []string{
|
||||
"scan",
|
||||
"--filter",
|
||||
"Type=='aws_iam_role' || Type=='aws_iam_policy_attachment'",
|
||||
"--tf-provider-version",
|
||||
"3.45.0",
|
||||
},
|
||||
Checks: []acceptance.AccCheck{
|
||||
{
|
||||
Env: map[string]string{
|
||||
"AWS_REGION": "us-east-1",
|
||||
},
|
||||
Check: func(result *test.ScanResult, stdout string, err error) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
result.AssertDriftCountTotal(0)
|
||||
result.AssertDeletedCount(0)
|
||||
result.AssertManagedCount(2)
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/aws" {
|
||||
version = "3.45.0"
|
||||
constraints = "3.45.0"
|
||||
hashes = [
|
||||
"h1:LKU/xfna87/p+hl5yTTW3dvOqWJp5JEM+Dt3nnvSDvA=",
|
||||
"zh:0fdbb3af75ff55807466533f97eb314556ec41a908a543d7cafb06546930f7c6",
|
||||
"zh:20656895744fa0f4607096b9681c77b2385f450b1577f9151d3070818378a724",
|
||||
"zh:390f316d00f25a5e45ef5410961fd05bf673068c1b701dc752d11df6d8e741d7",
|
||||
"zh:3da70f9de241d5f66ea9994ef1e0beddfdb005fa2d2ef6712392f57c5d2e4844",
|
||||
"zh:65de63cc0f97c85c28a19db560c546aa25f4f403dbf4783ac53c3918044cf180",
|
||||
"zh:6fc52072e5a66a5d0510aaa2b373a2697895f51398613c68619d8c0c95fc75f5",
|
||||
"zh:7c1da61092bd1206a020e3ee340ab11be8a4f9bb74e925ca1229ea5267fb3a62",
|
||||
"zh:94e533d86ce3c08e7102dcabe34ba32ae7fd7819fd0aedef28f48d29e635eae2",
|
||||
"zh:a3180d4826662e19e71cf20e925a2be8613a51f2f3f7b6d2643ac1418b976d58",
|
||||
"zh:c783df364928c77fd4dec5419533b125bebe2d50212c4ad609f83b701c2d981a",
|
||||
"zh:e1279bde388cb675d324584d965c6d22c3ec6890b13de76a50910a3bcd84ed64",
|
||||
]
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
version = "3.45.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "b" {
|
||||
name = "test_role"
|
||||
|
||||
# Terraform's "jsonencode" function converts a
|
||||
# Terraform expression result to valid JSON syntax.
|
||||
assume_role_policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Action = "sts:AssumeRole"
|
||||
Effect = "Allow"
|
||||
Sid = ""
|
||||
Principal = {
|
||||
Service = "ec2.amazonaws.com"
|
||||
}
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
resource "aws_iam_policy" "b" {
|
||||
name = "b"
|
||||
path = "/"
|
||||
description = "bbb"
|
||||
|
||||
# Terraform's "jsonencode" function converts a
|
||||
# Terraform expression result to valid JSON syntax.
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Action = [
|
||||
"ec2:Describe*",
|
||||
]
|
||||
Effect = "Allow"
|
||||
Resource = "*"
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy_attachment" "attach-1" {
|
||||
role = aws_iam_role.b.name
|
||||
policy_arn = aws_iam_policy.b.arn
|
||||
}
|
21
pkg/resource/aws/testdata/acc/aws_iam_role_with_managed_policies/.terraform.lock.hcl
vendored
Normal file
21
pkg/resource/aws/testdata/acc/aws_iam_role_with_managed_policies/.terraform.lock.hcl
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/aws" {
|
||||
version = "3.45.0"
|
||||
constraints = "3.45.0"
|
||||
hashes = [
|
||||
"h1:LKU/xfna87/p+hl5yTTW3dvOqWJp5JEM+Dt3nnvSDvA=",
|
||||
"zh:0fdbb3af75ff55807466533f97eb314556ec41a908a543d7cafb06546930f7c6",
|
||||
"zh:20656895744fa0f4607096b9681c77b2385f450b1577f9151d3070818378a724",
|
||||
"zh:390f316d00f25a5e45ef5410961fd05bf673068c1b701dc752d11df6d8e741d7",
|
||||
"zh:3da70f9de241d5f66ea9994ef1e0beddfdb005fa2d2ef6712392f57c5d2e4844",
|
||||
"zh:65de63cc0f97c85c28a19db560c546aa25f4f403dbf4783ac53c3918044cf180",
|
||||
"zh:6fc52072e5a66a5d0510aaa2b373a2697895f51398613c68619d8c0c95fc75f5",
|
||||
"zh:7c1da61092bd1206a020e3ee340ab11be8a4f9bb74e925ca1229ea5267fb3a62",
|
||||
"zh:94e533d86ce3c08e7102dcabe34ba32ae7fd7819fd0aedef28f48d29e635eae2",
|
||||
"zh:a3180d4826662e19e71cf20e925a2be8613a51f2f3f7b6d2643ac1418b976d58",
|
||||
"zh:c783df364928c77fd4dec5419533b125bebe2d50212c4ad609f83b701c2d981a",
|
||||
"zh:e1279bde388cb675d324584d965c6d22c3ec6890b13de76a50910a3bcd84ed64",
|
||||
]
|
||||
}
|
55
pkg/resource/aws/testdata/acc/aws_iam_role_with_managed_policies/terraform.tf
vendored
Normal file
55
pkg/resource/aws/testdata/acc/aws_iam_role_with_managed_policies/terraform.tf
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
version = "3.45.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "b" {
|
||||
name = "test_role"
|
||||
|
||||
managed_policy_arns = [aws_iam_policy.b.arn]
|
||||
|
||||
# Terraform's "jsonencode" function converts a
|
||||
# Terraform expression result to valid JSON syntax.
|
||||
assume_role_policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Action = "sts:AssumeRole"
|
||||
Effect = "Allow"
|
||||
Sid = ""
|
||||
Principal = {
|
||||
Service = "ec2.amazonaws.com"
|
||||
}
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
resource "aws_iam_policy" "b" {
|
||||
name = "b"
|
||||
path = "/"
|
||||
description = "bbb"
|
||||
|
||||
# Terraform's "jsonencode" function converts a
|
||||
# Terraform expression result to valid JSON syntax.
|
||||
policy = jsonencode({
|
||||
Version = "2012-10-17"
|
||||
Statement = [
|
||||
{
|
||||
Action = [
|
||||
"ec2:Describe*",
|
||||
]
|
||||
Effect = "Allow"
|
||||
Resource = "*"
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue