Repository: cloud-custodian/cloud-custodian

Issue:
EC2 Instance Profile  - Association and disassociation issue
As per the C7N _**set-instance-profile**_ is used to attach an IAM instance profile to an running or stopped EC2 instance. However, currently this action errors out if the instance already has an IAM role attached. It would be good to have a capability to replace current instance role with a new one (_**ReplaceIamInstanceProfileAssociation**_). The use case is, if the instance is wrongly assigned role custodian can check and correct it.  


Discussion:

Resolving patch:
diff --git a/c7n/resources/ec2.py b/c7n/resources/ec2.py
--- a/c7n/resources/ec2.py
+++ b/c7n/resources/ec2.py
@@ -1259,7 +1259,7 @@ def process(self, instances):
 
 @actions.register('set-instance-profile')
 class SetInstanceProfile(BaseAction, StateTransitionFilter):
-    """Sets (or removes) the instance profile for a running EC2 instance.
+    """Sets (add, modify, remove) the instance profile for a running EC2 instance.
 
     :Example:
 
@@ -1287,38 +1287,48 @@ class SetInstanceProfile(BaseAction, StateTransitionFilter):
         'ec2:DisassociateIamInstanceProfile',
         'iam:PassRole')
 
-    valid_origin_states = ('running', 'pending')
+    valid_origin_states = ('running', 'pending', 'stopped', 'stopping')
 
     def process(self, instances):
         instances = self.filter_instance_state(instances)
         if not len(instances):
             return
-        client = utils.local_session(
-            self.manager.session_factory).client('ec2')
-        profile_name = self.data.get('name', '')
+        client = utils.local_session(self.manager.session_factory).client('ec2')
+        profile_name = self.data.get('name')
+        profile_instances = [i for i in instances if i.get('IamInstanceProfile')]
+
+        associations = {
+            a['InstanceId']: (a['AssociationId'], a['IamInstanceProfile']['Arn'])
+            for a in client.describe_iam_instance_profile_associations(
+                Filters=[
+                    {'Name': 'instance-id',
+                     'Values': [i['InstanceId'] for i in profile_instances]},
+                    {'Name': 'state', 'Values': ['associating', 'associated']}]
+            ).get('IamInstanceProfileAssociations', ())}
 
         for i in instances:
-            if profile_name:
+            if profile_name and i['InstanceId'] not in associations:
                 client.associate_iam_instance_profile(
-                    IamInstanceProfile={'Name': self.data.get('name', '')},
+                    IamInstanceProfile={'Name': profile_name},
                     InstanceId=i['InstanceId'])
+                continue
+            # Removing profile and no profile on instance.
+            elif profile_name is None and i['InstanceId'] not in associations:
+                continue
+
+            p_assoc_id, p_arn = associations[i['InstanceId']]
+
+            # Already associated to target profile, skip
+            if profile_name and p_arn.endswith('/%s' % profile_name):
+                continue
+
+            if profile_name is None:
+                client.disassociate_iam_instance_profile(
+                    AssociationId=p_assoc_id)
             else:
-                response = client.describe_iam_instance_profile_associations(
-                    Filters=[
-                        {
-                            'Name': 'instance-id',
-                            'Values': [i['InstanceId']],
-                        },
-                        {
-                            'Name': 'state',
-                            'Values': ['associating', 'associated']
-                        }
-                    ]
-                )
-                for a in response['IamInstanceProfileAssociations']:
-                    client.disassociate_iam_instance_profile(
-                        AssociationId=a['AssociationId']
-                    )
+                client.replace_iam_instance_profile_association(
+                    IamInstanceProfile={'Name': profile_name},
+                    AssociationId=p_assoc_id)
 
         return instances
 


Test patch:
diff --git a/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.DescribeInstances_1.json b/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.DescribeInstances_1.json
deleted file mode 100644
--- a/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.DescribeInstances_1.json
+++ /dev/null
@@ -1,145 +0,0 @@
-{
-    "status_code": 200, 
-    "data": {
-        "Reservations": [
-            {
-                "OwnerId": "0", 
-                "ReservationId": "r-04df2e5d9eea3e407", 
-                "Groups": [], 
-                "Instances": [
-                    {
-                        "Monitoring": {
-                            "State": "disabled"
-                        }, 
-                        "PublicDnsName": "", 
-                        "State": {
-                            "Code": 16, 
-                            "Name": "running"
-                        }, 
-                        "EbsOptimized": false, 
-                        "LaunchTime": {
-                            "hour": 18, 
-                            "__class__": "datetime", 
-                            "month": 5, 
-                            "second": 18, 
-                            "microsecond": 0, 
-                            "year": 2017, 
-                            "day": 23, 
-                            "minute": 6
-                        }, 
-                        "PrivateIpAddress": "10.0.0.38", 
-                        "ProductCodes": [], 
-                        "VpcId": "vpc-01234567", 
-                        "StateTransitionReason": "", 
-                        "InstanceId": "i-00fe7967fb7167c62", 
-                        "ImageId": "ami-01234567", 
-                        "PrivateDnsName": "ip-10-0-0-38.us-west-1.compute.internal", 
-                        "SecurityGroups": [
-                            {
-                                "GroupName": "default", 
-                                "GroupId": "sg-7e41d819"
-                            }
-                        ], 
-                        "ClientToken": "CZSyv1495562777688", 
-                        "SubnetId": "subnet-d832b380", 
-                        "InstanceType": "t2.micro", 
-                        "NetworkInterfaces": [
-                            {
-                                "Status": "in-use", 
-                                "MacAddress": "06:78:d8:5a:71:5a", 
-                                "SourceDestCheck": true, 
-                                "VpcId": "vpc-01234567", 
-                                "Description": "Primary network interface", 
-                                "NetworkInterfaceId": "eni-ee61adee", 
-                                "PrivateIpAddresses": [
-                                    {
-                                        "PrivateDnsName": "ip-10-0-0-38.us-west-1.compute.internal", 
-                                        "Primary": true, 
-                                        "PrivateIpAddress": "10.0.0.38"
-                                    }
-                                ], 
-                                "PrivateDnsName": "ip-10-0-0-38.us-west-1.compute.internal", 
-                                "Attachment": {
-                                    "Status": "attached", 
-                                    "DeviceIndex": 0, 
-                                    "DeleteOnTermination": true, 
-                                    "AttachmentId": "eni-attach-05848ded", 
-                                    "AttachTime": {
-                                        "hour": 18, 
-                                        "__class__": "datetime", 
-                                        "month": 5, 
-                                        "second": 18, 
-                                        "microsecond": 0, 
-                                        "year": 2017, 
-                                        "day": 23, 
-                                        "minute": 6
-                                    }
-                                }, 
-                                "Groups": [
-                                    {
-                                        "GroupName": "default", 
-                                        "GroupId": "sg-7e41d819"
-                                    }
-                                ], 
-                                "Ipv6Addresses": [], 
-                                "SubnetId": "subnet-d832b380", 
-                                "OwnerId": "0", 
-                                "PrivateIpAddress": "10.0.0.38"
-                            }
-                        ], 
-                        "SourceDestCheck": true, 
-                        "Placement": {
-                            "Tenancy": "default", 
-                            "GroupName": "", 
-                            "AvailabilityZone": "us-west-1c"
-                        }, 
-                        "Hypervisor": "xen", 
-                        "BlockDeviceMappings": [
-                            {
-                                "DeviceName": "/dev/xvda", 
-                                "Ebs": {
-                                    "Status": "attached", 
-                                    "DeleteOnTermination": true, 
-                                    "VolumeId": "vol-0b6158b4b95bfde5f", 
-                                    "AttachTime": {
-                                        "hour": 18, 
-                                        "__class__": "datetime", 
-                                        "month": 5, 
-                                        "second": 19, 
-                                        "microsecond": 0, 
-                                        "year": 2017, 
-                                        "day": 23, 
-                                        "minute": 6
-                                    }
-                                }
-                            }
-                        ], 
-                        "Architecture": "x86_64", 
-                        "RootDeviceType": "ebs", 
-                        "RootDeviceName": "/dev/xvda", 
-                        "VirtualizationType": "hvm", 
-                        "Tags": [
-                            {
-                                "Value": "MissingInstanceProfile", 
-                                "Key": "Name"
-                            }
-                        ], 
-                        "AmiLaunchIndex": 0
-                    }
-                ]
-            } 
-        ], 
-        "ResponseMetadata": {
-            "RetryAttempts": 0, 
-            "HTTPStatusCode": 200, 
-            "RequestId": "078c5a3f-4647-4055-8622-19101e80cdd2", 
-            "HTTPHeaders": {
-                "transfer-encoding": "chunked", 
-                "vary": "Accept-Encoding", 
-                "server": "AmazonEC2", 
-                "content-type": "text/xml;charset=UTF-8", 
-                "date": "Tue, 23 May 2017 18:10:43 GMT"
-            }
-        }
-    }
-}
diff --git a/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.DescribeInstances_2.json b/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.DescribeInstances_2.json
deleted file mode 100644
--- a/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.DescribeInstances_2.json
+++ /dev/null
@@ -1,149 +0,0 @@
-{
-    "status_code": 200, 
-    "data": {
-        "Reservations": [
-            {
-                "OwnerId": "0", 
-                "ReservationId": "r-04df2e5d9eea3e407", 
-                "Groups": [], 
-                "Instances": [
-                    {
-                        "Monitoring": {
-                            "State": "disabled"
-                        }, 
-                        "PublicDnsName": "", 
-                        "State": {
-                            "Code": 16, 
-                            "Name": "running"
-                        }, 
-                        "EbsOptimized": false, 
-                        "LaunchTime": {
-                            "hour": 18, 
-                            "__class__": "datetime", 
-                            "month": 5, 
-                            "second": 18, 
-                            "microsecond": 0, 
-                            "year": 2017, 
-                            "day": 23, 
-                            "minute": 6
-                        }, 
-                        "PrivateIpAddress": "10.0.0.38", 
-                        "ProductCodes": [], 
-                        "VpcId": "vpc-01234567", 
-                        "StateTransitionReason": "", 
-                        "InstanceId": "i-00fe7967fb7167c62", 
-                        "ImageId": "ami-01234567", 
-                        "PrivateDnsName": "ip-10-0-0-38.us-west-1.compute.internal", 
-                        "SecurityGroups": [
-                            {
-                                "GroupName": "default", 
-                                "GroupId": "sg-7e41d819"
-                            }
-                        ], 
-                        "ClientToken": "CZSyv1495562777688", 
-                        "SubnetId": "subnet-d832b380", 
-                        "InstanceType": "t2.micro", 
-                        "NetworkInterfaces": [
-                            {
-                                "Status": "in-use", 
-                                "MacAddress": "06:78:d8:5a:71:5a", 
-                                "SourceDestCheck": true, 
-                                "VpcId": "vpc-01234567", 
-                                "Description": "Primary network interface", 
-                                "NetworkInterfaceId": "eni-ee61adee", 
-                                "PrivateIpAddresses": [
-                                    {
-                                        "PrivateDnsName": "ip-10-0-0-38.us-west-1.compute.internal", 
-                                        "Primary": true, 
-                                        "PrivateIpAddress": "10.0.0.38"
-                                    }
-                                ], 
-                                "PrivateDnsName": "ip-10-0-0-38.us-west-1.compute.internal", 
-                                "Attachment": {
-                                    "Status": "attached", 
-                                    "DeviceIndex": 0, 
-                                    "DeleteOnTermination": true, 
-                                    "AttachmentId": "eni-attach-05848ded", 
-                                    "AttachTime": {
-                                        "hour": 18, 
-                                        "__class__": "datetime", 
-                                        "month": 5, 
-                                        "second": 18, 
-                                        "microsecond": 0, 
-                                        "year": 2017, 
-                                        "day": 23, 
-                                        "minute": 6
-                                    }
-                                }, 
-                                "Groups": [
-                                    {
-                                        "GroupName": "default", 
-                                        "GroupId": "sg-7e41d819"
-                                    }
-                                ], 
-                                "Ipv6Addresses": [], 
-                                "SubnetId": "subnet-d832b380", 
-                                "OwnerId": "0", 
-                                "PrivateIpAddress": "10.0.0.38"
-                            }
-                        ], 
-                        "SourceDestCheck": true, 
-                        "Placement": {
-                            "Tenancy": "default", 
-                            "GroupName": "", 
-                            "AvailabilityZone": "us-west-1c"
-                        }, 
-                        "Hypervisor": "xen", 
-                        "BlockDeviceMappings": [
-                            {
-                                "DeviceName": "/dev/xvda", 
-                                "Ebs": {
-                                    "Status": "attached", 
-                                    "DeleteOnTermination": true, 
-                                    "VolumeId": "vol-0b6158b4b95bfde5f", 
-                                    "AttachTime": {
-                                        "hour": 18, 
-                                        "__class__": "datetime", 
-                                        "month": 5, 
-                                        "second": 19, 
-                                        "microsecond": 0, 
-                                        "year": 2017, 
-                                        "day": 23, 
-                                        "minute": 6
-                                    }
-                                }
-                            }
-                        ], 
-                        "Architecture": "x86_64", 
-                        "RootDeviceType": "ebs", 
-                        "IamInstanceProfile": {
-                            "Id": "AIPAI5AW5I5CLIOSXKFK6", 
-                            "Arn": "arn:aws:iam::0:instance-profile/ec2-default"
-                        }, 
-                        "RootDeviceName": "/dev/xvda", 
-                        "VirtualizationType": "hvm", 
-                        "Tags": [
-                            {
-                                "Value": "MissingInstanceProfile", 
-                                "Key": "Name"
-                            }
-                        ], 
-                        "AmiLaunchIndex": 0
-                    }
-                ]
-            }
-        ], 
-        "ResponseMetadata": {
-            "RetryAttempts": 0, 
-            "HTTPStatusCode": 200, 
-            "RequestId": "6d1c5de2-2625-4278-9f22-98fcc75618e1", 
-            "HTTPHeaders": {
-                "transfer-encoding": "chunked", 
-                "vary": "Accept-Encoding", 
-                "server": "AmazonEC2", 
-                "content-type": "text/xml;charset=UTF-8", 
-                "date": "Tue, 23 May 2017 18:10:45 GMT"
-            }
-        }
-    }
-}
diff --git a/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.AssociateIamInstanceProfile_1.json b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.AssociateIamInstanceProfile_1.json
similarity index 57%
rename from tests/data/placebo/test_ec2_set_instance_profile_association/ec2.AssociateIamInstanceProfile_1.json
rename to tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.AssociateIamInstanceProfile_1.json
--- a/tests/data/placebo/test_ec2_set_instance_profile_association/ec2.AssociateIamInstanceProfile_1.json
+++ b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.AssociateIamInstanceProfile_1.json
@@ -4,23 +4,23 @@
         "ResponseMetadata": {
             "RetryAttempts": 0, 
             "HTTPStatusCode": 200, 
-            "RequestId": "66b6d48c-3b0d-4305-97ce-e57416b67a93",
+            "RequestId": "a11b9cef-09e7-46e4-a993-d46f4075f1e6", 
             "HTTPHeaders": {
                 "transfer-encoding": "chunked", 
                 "vary": "Accept-Encoding", 
                 "server": "AmazonEC2", 
                 "content-type": "text/xml;charset=UTF-8", 
-                "date": "Tue, 23 May 2017 18:10:44 GMT"
+                "date": "Fri, 06 Jul 2018 10:20:20 GMT"
             }
-        },
+        }, 
         "IamInstanceProfileAssociation": {
-            "InstanceId": "i-00fe7967fb7167c62",
+            "InstanceId": "i-0aef5d5ffb60c8615", 
             "State": "associating", 
-            "AssociationId": "iip-assoc-0373c3be6ef0e2897",
+            "AssociationId": "iip-assoc-029e2b5bd902df18e", 
             "IamInstanceProfile": {
-                "Id": "AIPAI5AW5I5CLIOSXKFK6", 
-                "Arn": "arn:aws:iam::0:instance-profile/ec2-default"
+                "Id": "AIPAJJOHXXHTHDZB5BVFE", 
+                "Arn": "arn:aws:iam::644160558196:instance-profile/ecsInstanceRole"
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeIamInstanceProfileAssociations_1.json b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeIamInstanceProfileAssociations_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeIamInstanceProfileAssociations_1.json
@@ -0,0 +1,37 @@
+{
+    "status_code": 200, 
+    "data": {
+        "IamInstanceProfileAssociations": [
+            {
+                "InstanceId": "i-01b7ee380879d3fd8", 
+                "State": "associated", 
+                "AssociationId": "iip-assoc-028de94dde8c8ccf0", 
+                "IamInstanceProfile": {
+                    "Id": "AIPAJO5EMJ3YEMNUF5ZWM", 
+                    "Arn": "arn:aws:iam::644160558196:instance-profile/CloudCustodianRole"
+                }
+            }, 
+            {
+                "InstanceId": "i-06305b4b9f5e3f8b8", 
+                "State": "associated", 
+                "AssociationId": "iip-assoc-064944afb6d8735a3", 
+                "IamInstanceProfile": {
+                    "Id": "AIPAJJOHXXHTHDZB5BVFE", 
+                    "Arn": "arn:aws:iam::644160558196:instance-profile/ecsInstanceRole"
+                }
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "2c22ba6a-a2cf-40ab-a97e-98f420d1e46e", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Fri, 06 Jul 2018 10:20:20 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeIamInstanceProfileAssociations_2.json b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeIamInstanceProfileAssociations_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeIamInstanceProfileAssociations_2.json
@@ -0,0 +1,46 @@
+{
+    "status_code": 200, 
+    "data": {
+        "IamInstanceProfileAssociations": [
+            {
+                "InstanceId": "i-06305b4b9f5e3f8b8", 
+                "State": "associated", 
+                "AssociationId": "iip-assoc-064944afb6d8735a3", 
+                "IamInstanceProfile": {
+                    "Id": "AIPAJJOHXXHTHDZB5BVFE", 
+                    "Arn": "arn:aws:iam::644160558196:instance-profile/ecsInstanceRole"
+                }
+            }, 
+            {
+                "InstanceId": "i-0aef5d5ffb60c8615", 
+                "State": "associating", 
+                "AssociationId": "iip-assoc-029e2b5bd902df18e", 
+                "IamInstanceProfile": {
+                    "Id": "AIPAJJOHXXHTHDZB5BVFE", 
+                    "Arn": "arn:aws:iam::644160558196:instance-profile/ecsInstanceRole"
+                }
+            }, 
+            {
+                "InstanceId": "i-01b7ee380879d3fd8", 
+                "State": "associating", 
+                "AssociationId": "iip-assoc-0499952a50fbcbbae", 
+                "IamInstanceProfile": {
+                    "Id": "AIPAJJOHXXHTHDZB5BVFE", 
+                    "Arn": "arn:aws:iam::644160558196:instance-profile/ecsInstanceRole"
+                }
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "d1c50e86-e8dd-4dcf-a65c-94420ec0bc65", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Fri, 06 Jul 2018 10:20:21 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeInstances_1.json b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeInstances_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.DescribeInstances_1.json
@@ -0,0 +1,458 @@
+{
+    "status_code": 200, 
+    "data": {
+        "Reservations": [
+            {
+                "Instances": [
+                    {
+                        "Monitoring": {
+                            "State": "disabled"
+                        }, 
+                        "PublicDnsName": "ec2-107-23-173-5.compute-1.amazonaws.com", 
+                        "State": {
+                            "Code": 16, 
+                            "Name": "running"
+                        }, 
+                        "EbsOptimized": false, 
+                        "LaunchTime": {
+                            "hour": 9, 
+                            "__class__": "datetime", 
+                            "month": 7, 
+                            "second": 11, 
+                            "microsecond": 0, 
+                            "year": 2018, 
+                            "day": 6, 
+                            "minute": 51
+                        }, 
+                        "PublicIpAddress": "107.23.173.5", 
+                        "PrivateIpAddress": "172.31.62.252", 
+                        "ProductCodes": [], 
+                        "VpcId": "vpc-d2d616b5", 
+                        "CpuOptions": {
+                            "CoreCount": 1, 
+                            "ThreadsPerCore": 1
+                        }, 
+                        "StateTransitionReason": "", 
+                        "InstanceId": "i-01b7ee380879d3fd8", 
+                        "EnaSupport": true, 
+                        "ImageId": "ami-a4dc46db", 
+                        "PrivateDnsName": "ip-172-31-62-252.ec2.internal", 
+                        "SecurityGroups": [
+                            {
+                                "GroupName": "default", 
+                                "GroupId": "sg-6c7fa917"
+                            }
+                        ], 
+                        "ClientToken": "", 
+                        "SubnetId": "subnet-3a334610", 
+                        "InstanceType": "t2.micro", 
+                        "NetworkInterfaces": [
+                            {
+                                "Status": "in-use", 
+                                "MacAddress": "12:ac:b5:a7:98:ba", 
+                                "SourceDestCheck": true, 
+                                "VpcId": "vpc-d2d616b5", 
+                                "Description": "Primary network interface", 
+                                "NetworkInterfaceId": "eni-cbecab68", 
+                                "PrivateIpAddresses": [
+                                    {
+                                        "PrivateDnsName": "ip-172-31-62-252.ec2.internal", 
+                                        "PrivateIpAddress": "172.31.62.252", 
+                                        "Primary": true, 
+                                        "Association": {
+                                            "PublicIp": "107.23.173.5", 
+                                            "PublicDnsName": "ec2-107-23-173-5.compute-1.amazonaws.com", 
+                                            "IpOwnerId": "amazon"
+                                        }
+                                    }
+                                ], 
+                                "PrivateDnsName": "ip-172-31-62-252.ec2.internal", 
+                                "Attachment": {
+                                    "Status": "attached", 
+                                    "DeviceIndex": 0, 
+                                    "DeleteOnTermination": true, 
+                                    "AttachmentId": "eni-attach-672e96dd", 
+                                    "AttachTime": {
+                                        "hour": 9, 
+                                        "__class__": "datetime", 
+                                        "month": 7, 
+                                        "second": 11, 
+                                        "microsecond": 0, 
+                                        "year": 2018, 
+                                        "day": 6, 
+                                        "minute": 51
+                                    }
+                                }, 
+                                "Groups": [
+                                    {
+                                        "GroupName": "default", 
+                                        "GroupId": "sg-6c7fa917"
+                                    }
+                                ], 
+                                "Ipv6Addresses": [], 
+                                "OwnerId": "644160558196", 
+                                "PrivateIpAddress": "172.31.62.252", 
+                                "SubnetId": "subnet-3a334610", 
+                                "Association": {
+                                    "PublicIp": "107.23.173.5", 
+                                    "PublicDnsName": "ec2-107-23-173-5.compute-1.amazonaws.com", 
+                                    "IpOwnerId": "amazon"
+                                }
+                            }
+                        ], 
+                        "SourceDestCheck": true, 
+                        "Placement": {
+                            "Tenancy": "default", 
+                            "GroupName": "", 
+                            "AvailabilityZone": "us-east-1d"
+                        }, 
+                        "Hypervisor": "xen", 
+                        "BlockDeviceMappings": [
+                            {
+                                "DeviceName": "/dev/sda1", 
+                                "Ebs": {
+                                    "Status": "attached", 
+                                    "DeleteOnTermination": true, 
+                                    "VolumeId": "vol-067ce2d48d58171af", 
+                                    "AttachTime": {
+                                        "hour": 9, 
+                                        "__class__": "datetime", 
+                                        "month": 7, 
+                                        "second": 12, 
+                                        "microsecond": 0, 
+                                        "year": 2018, 
+                                        "day": 6, 
+                                        "minute": 51
+                                    }
+                                }
+                            }
+                        ], 
+                        "Architecture": "x86_64", 
+                        "RootDeviceType": "ebs", 
+                        "IamInstanceProfile": {
+                            "Id": "AIPAJO5EMJ3YEMNUF5ZWM", 
+                            "Arn": "arn:aws:iam::644160558196:instance-profile/CloudCustodianRole"
+                        }, 
+                        "RootDeviceName": "/dev/sda1", 
+                        "VirtualizationType": "hvm", 
+                        "Tags": [
+                            {
+                                "Value": "kapil", 
+                                "Key": "CreatorName"
+                            }, 
+                            {
+                                "Value": "role-test", 
+                                "Key": "Name"
+                            }
+                        ], 
+                        "AmiLaunchIndex": 1
+                    }, 
+                    {
+                        "Monitoring": {
+                            "State": "disabled"
+                        }, 
+                        "PublicDnsName": "ec2-52-87-159-205.compute-1.amazonaws.com", 
+                        "State": {
+                            "Code": 16, 
+                            "Name": "running"
+                        }, 
+                        "EbsOptimized": false, 
+                        "LaunchTime": {
+                            "hour": 9, 
+                            "__class__": "datetime", 
+                            "month": 7, 
+                            "second": 11, 
+                            "microsecond": 0, 
+                            "year": 2018, 
+                            "day": 6, 
+                            "minute": 51
+                        }, 
+                        "PublicIpAddress": "52.87.159.205", 
+                        "PrivateIpAddress": "172.31.49.50", 
+                        "ProductCodes": [], 
+                        "VpcId": "vpc-d2d616b5", 
+                        "CpuOptions": {
+                            "CoreCount": 1, 
+                            "ThreadsPerCore": 1
+                        }, 
+                        "StateTransitionReason": "", 
+                        "InstanceId": "i-0aef5d5ffb60c8615", 
+                        "EnaSupport": true, 
+                        "ImageId": "ami-a4dc46db", 
+                        "PrivateDnsName": "ip-172-31-49-50.ec2.internal", 
+                        "SecurityGroups": [
+                            {
+                                "GroupName": "default", 
+                                "GroupId": "sg-6c7fa917"
+                            }
+                        ], 
+                        "ClientToken": "", 
+                        "SubnetId": "subnet-3a334610", 
+                        "InstanceType": "t2.micro", 
+                        "NetworkInterfaces": [
+                            {
+                                "Status": "in-use", 
+                                "MacAddress": "12:19:1e:58:56:7c", 
+                                "SourceDestCheck": true, 
+                                "VpcId": "vpc-d2d616b5", 
+                                "Description": "Primary network interface", 
+                                "NetworkInterfaceId": "eni-caecab69", 
+                                "PrivateIpAddresses": [
+                                    {
+                                        "PrivateDnsName": "ip-172-31-49-50.ec2.internal", 
+                                        "PrivateIpAddress": "172.31.49.50", 
+                                        "Primary": true, 
+                                        "Association": {
+                                            "PublicIp": "52.87.159.205", 
+                                            "PublicDnsName": "ec2-52-87-159-205.compute-1.amazonaws.com", 
+                                            "IpOwnerId": "amazon"
+                                        }
+                                    }
+                                ], 
+                                "PrivateDnsName": "ip-172-31-49-50.ec2.internal", 
+                                "Attachment": {
+                                    "Status": "attached", 
+                                    "DeviceIndex": 0, 
+                                    "DeleteOnTermination": true, 
+                                    "AttachmentId": "eni-attach-b82f9702", 
+                                    "AttachTime": {
+                                        "hour": 9, 
+                                        "__class__": "datetime", 
+                                        "month": 7, 
+                                        "second": 11, 
+                                        "microsecond": 0, 
+                                        "year": 2018, 
+                                        "day": 6, 
+                                        "minute": 51
+                                    }
+                                }, 
+                                "Groups": [
+                                    {
+                                        "GroupName": "default", 
+                                        "GroupId": "sg-6c7fa917"
+                                    }
+                                ], 
+                                "Ipv6Addresses": [], 
+                                "OwnerId": "644160558196", 
+                                "PrivateIpAddress": "172.31.49.50", 
+                                "SubnetId": "subnet-3a334610", 
+                                "Association": {
+                                    "PublicIp": "52.87.159.205", 
+                                    "PublicDnsName": "ec2-52-87-159-205.compute-1.amazonaws.com", 
+                                    "IpOwnerId": "amazon"
+                                }
+                            }
+                        ], 
+                        "SourceDestCheck": true, 
+                        "Placement": {
+                            "Tenancy": "default", 
+                            "GroupName": "", 
+                            "AvailabilityZone": "us-east-1d"
+                        }, 
+                        "Hypervisor": "xen", 
+                        "BlockDeviceMappings": [
+                            {
+                                "DeviceName": "/dev/sda1", 
+                                "Ebs": {
+                                    "Status": "attached", 
+                                    "DeleteOnTermination": true, 
+                                    "VolumeId": "vol-0e0a622f200edb20d", 
+                                    "AttachTime": {
+                                        "hour": 9, 
+                                        "__class__": "datetime", 
+                                        "month": 7, 
+                                        "second": 12, 
+                                        "microsecond": 0, 
+                                        "year": 2018, 
+                                        "day": 6, 
+                                        "minute": 51
+                                    }
+                                }
+                            }
+                        ], 
+                        "Architecture": "x86_64", 
+                        "RootDeviceType": "ebs", 
+                        "RootDeviceName": "/dev/sda1", 
+                        "VirtualizationType": "hvm", 
+                        "Tags": [
+                            {
+                                "Value": "kapil", 
+                                "Key": "CreatorName"
+                            }, 
+                            {
+                                "Value": "role-test", 
+                                "Key": "Name"
+                            }
+                        ], 
+                        "AmiLaunchIndex": 0
+                    }
+                ], 
+                "ReservationId": "r-0def8eae4cdd72052", 
+                "Groups": [], 
+                "OwnerId": "644160558196"
+            }, 
+            {
+                "Instances": [
+                    {
+                        "Monitoring": {
+                            "State": "disabled"
+                        }, 
+                        "PublicDnsName": "ec2-54-87-164-90.compute-1.amazonaws.com", 
+                        "State": {
+                            "Code": 16, 
+                            "Name": "running"
+                        }, 
+                        "EbsOptimized": false, 
+                        "LaunchTime": {
+                            "hour": 9, 
+                            "__class__": "datetime", 
+                            "month": 7, 
+                            "second": 25, 
+                            "microsecond": 0, 
+                            "year": 2018, 
+                            "day": 6, 
+                            "minute": 41
+                        }, 
+                        "PublicIpAddress": "54.87.164.90", 
+                        "PrivateIpAddress": "172.31.52.141", 
+                        "ProductCodes": [], 
+                        "VpcId": "vpc-d2d616b5", 
+                        "CpuOptions": {
+                            "CoreCount": 1, 
+                            "ThreadsPerCore": 1
+                        }, 
+                        "StateTransitionReason": "", 
+                        "InstanceId": "i-06305b4b9f5e3f8b8", 
+                        "EnaSupport": true, 
+                        "ImageId": "ami-a4dc46db", 
+                        "PrivateDnsName": "ip-172-31-52-141.ec2.internal", 
+                        "SecurityGroups": [
+                            {
+                                "GroupName": "default", 
+                                "GroupId": "sg-6c7fa917"
+                            }
+                        ], 
+                        "ClientToken": "", 
+                        "SubnetId": "subnet-3a334610", 
+                        "InstanceType": "t2.micro", 
+                        "NetworkInterfaces": [
+                            {
+                                "Status": "in-use", 
+                                "MacAddress": "12:6e:ed:f4:51:68", 
+                                "SourceDestCheck": true, 
+                                "VpcId": "vpc-d2d616b5", 
+                                "Description": "", 
+                                "NetworkInterfaceId": "eni-e6d69145", 
+                                "PrivateIpAddresses": [
+                                    {
+                                        "PrivateDnsName": "ip-172-31-52-141.ec2.internal", 
+                                        "PrivateIpAddress": "172.31.52.141", 
+                                        "Primary": true, 
+                                        "Association": {
+                                            "PublicIp": "54.87.164.90", 
+                                            "PublicDnsName": "ec2-54-87-164-90.compute-1.amazonaws.com", 
+                                            "IpOwnerId": "amazon"
+                                        }
+                                    }
+                                ], 
+                                "PrivateDnsName": "ip-172-31-52-141.ec2.internal", 
+                                "Attachment": {
+                                    "Status": "attached", 
+                                    "DeviceIndex": 0, 
+                                    "DeleteOnTermination": true, 
+                                    "AttachmentId": "eni-attach-c63e867c", 
+                                    "AttachTime": {
+                                        "hour": 9, 
+                                        "__class__": "datetime", 
+                                        "month": 7, 
+                                        "second": 25, 
+                                        "microsecond": 0, 
+                                        "year": 2018, 
+                                        "day": 6, 
+                                        "minute": 41
+                                    }
+                                }, 
+                                "Groups": [
+                                    {
+                                        "GroupName": "default", 
+                                        "GroupId": "sg-6c7fa917"
+                                    }
+                                ], 
+                                "Ipv6Addresses": [], 
+                                "OwnerId": "644160558196", 
+                                "PrivateIpAddress": "172.31.52.141", 
+                                "SubnetId": "subnet-3a334610", 
+                                "Association": {
+                                    "PublicIp": "54.87.164.90", 
+                                    "PublicDnsName": "ec2-54-87-164-90.compute-1.amazonaws.com", 
+                                    "IpOwnerId": "amazon"
+                                }
+                            }
+                        ], 
+                        "SourceDestCheck": true, 
+                        "Placement": {
+                            "Tenancy": "default", 
+                            "GroupName": "", 
+                            "AvailabilityZone": "us-east-1d"
+                        }, 
+                        "Hypervisor": "xen", 
+                        "BlockDeviceMappings": [
+                            {
+                                "DeviceName": "/dev/sda1", 
+                                "Ebs": {
+                                    "Status": "attached", 
+                                    "DeleteOnTermination": true, 
+                                    "VolumeId": "vol-0966d74aaa855f77c", 
+                                    "AttachTime": {
+                                        "hour": 9, 
+                                        "__class__": "datetime", 
+                                        "month": 7, 
+                                        "second": 26, 
+                                        "microsecond": 0, 
+                                        "year": 2018, 
+                                        "day": 6, 
+                                        "minute": 41
+                                    }
+                                }
+                            }
+                        ], 
+                        "Architecture": "x86_64", 
+                        "RootDeviceType": "ebs", 
+                        "IamInstanceProfile": {
+                            "Id": "AIPAJJOHXXHTHDZB5BVFE", 
+                            "Arn": "arn:aws:iam::644160558196:instance-profile/ecsInstanceRole"
+                        }, 
+                        "RootDeviceName": "/dev/sda1", 
+                        "VirtualizationType": "hvm", 
+                        "Tags": [
+                            {
+                                "Value": "kapil", 
+                                "Key": "CreatorName"
+                            }, 
+                            {
+                                "Value": "role-test", 
+                                "Key": "Name"
+                            }
+                        ], 
+                        "AmiLaunchIndex": 0
+                    }
+                ], 
+                "ReservationId": "r-0fa6666a3aeaad1f2", 
+                "Groups": [], 
+                "OwnerId": "644160558196"
+            }
+        ], 
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "94014ee0-dad3-4233-87e2-cabc4709d3a5", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Fri, 06 Jul 2018 10:20:19 GMT"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.ReplaceIamInstanceProfileAssociation_1.json b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.ReplaceIamInstanceProfileAssociation_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_ec2_set_instance_profile_existing/ec2.ReplaceIamInstanceProfileAssociation_1.json
@@ -0,0 +1,26 @@
+{
+    "status_code": 200, 
+    "data": {
+        "ResponseMetadata": {
+            "RetryAttempts": 0, 
+            "HTTPStatusCode": 200, 
+            "RequestId": "ff569f93-6a5a-4788-bceb-b887771439b7", 
+            "HTTPHeaders": {
+                "transfer-encoding": "chunked", 
+                "vary": "Accept-Encoding", 
+                "server": "AmazonEC2", 
+                "content-type": "text/xml;charset=UTF-8", 
+                "date": "Fri, 06 Jul 2018 10:20:20 GMT"
+            }
+        }, 
+        "IamInstanceProfileAssociation": {
+            "InstanceId": "i-01b7ee380879d3fd8", 
+            "State": "associating", 
+            "AssociationId": "iip-assoc-0499952a50fbcbbae", 
+            "IamInstanceProfile": {
+                "Id": "AIPAJJOHXXHTHDZB5BVFE", 
+                "Arn": "arn:aws:iam::644160558196:instance-profile/ecsInstanceRole"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/test_ec2.py b/tests/test_ec2.py
--- a/tests/test_ec2.py
+++ b/tests/test_ec2.py
@@ -913,36 +913,43 @@ def test_ec2_snapshot_copy_tags(self):
 
 class TestSetInstanceProfile(BaseTest):
 
-    def test_ec2_set_instance_profile_assocation(self):
-        session_factory = self.replay_flight_data(
-            "test_ec2_set_instance_profile_association"
-        )
-        policy = self.load_policy(
-            {
-                "name": "ec2-test-set-instance-profile-association",
-                "resource": "ec2",
-                "filters": [
-                    {"tag:Name": "MissingInstanceProfile"},
-                    {"IamInstanceProfile": "absent"},
-                ],
-                "actions": [{"type": "set-instance-profile", "name": "ec2-default"}],
-            },
-            session_factory=session_factory,
-        )
-        resources = policy.run()
-        self.assertGreaterEqual(len(resources), 1)
-        ec2 = session_factory().client("ec2")
-        resources = ec2.describe_instances(
-            InstanceIds=[r["InstanceId"] for r in resources]
-        )
-
-        for r in resources["Reservations"]:
-            for i in r["Instances"]:
-                self.assertIn("IamInstanceProfile", i)
-                self.assertIn("Arn", i["IamInstanceProfile"])
-                self.assertIn(
-                    ":instance-profile/ec2-default", i["IamInstanceProfile"]["Arn"]
-                )
+    def test_ec2_set_instance_profile_existing(self):
+        factory = self.replay_flight_data(
+            'test_ec2_set_instance_profile_existing')
+        p = self.load_policy({
+            'name': 'ec2-set-profile-extant',
+            'resource': 'ec2',
+            'filters': [{'tag:Name': 'role-test'}],
+            'actions': [{
+                'type': 'set-instance-profile',
+                'name': 'ecsInstanceRole'}]}, session_factory=factory)
+        client = factory().client('ec2')
+        resources = p.run()
+        # 3 instances covering no role, target role, different role.
+        self.assertEqual(len(resources), 3)
+        previous_associations = {
+            i['InstanceId']: i.get('IamInstanceProfile', {}).get('Arn')
+            for i in resources}
+        self.assertEqual(
+            previous_associations,
+            {u'i-01b7ee380879d3fd8': u'arn:aws:iam::644160558196:instance-profile/CloudCustodianRole', # noqa
+             u'i-06305b4b9f5e3f8b8': u'arn:aws:iam::644160558196:instance-profile/ecsInstanceRole',
+             u'i-0aef5d5ffb60c8615': None})
+
+        # verify changes
+        associations = {
+            a['InstanceId']: a['IamInstanceProfile']['Arn']
+            for a in client.describe_iam_instance_profile_associations(
+                Filters=[
+                    {'Name': 'instance-id',
+                     'Values': [i['InstanceId'] for i in resources]},
+                    {'Name': 'state', 'Values': ['associating', 'associated']}]
+            ).get('IamInstanceProfileAssociations', ())}
+        self.assertEqual(
+            associations,
+            {'i-01b7ee380879d3fd8': 'arn:aws:iam::644160558196:instance-profile/ecsInstanceRole',
+             'i-06305b4b9f5e3f8b8': 'arn:aws:iam::644160558196:instance-profile/ecsInstanceRole',
+             'i-0aef5d5ffb60c8615': 'arn:aws:iam::644160558196:instance-profile/ecsInstanceRole'})
 
     def test_ec2_set_instance_profile_disassocation(self):
         session_factory = self.replay_flight_data(