Skip to content

Commit

Permalink
update to not omit existing services from list
Browse files Browse the repository at this point in the history
  • Loading branch information
badra001 committed Apr 14, 2026
1 parent 70dde3f commit b5e2a48
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 23 deletions.
11 changes: 9 additions & 2 deletions local-app/python-tools/aws-service-linked-roles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,15 @@ resource "aws_iam_service_linked_role" "roles" {
}
```

## Changelog
## CHANGELOG

### **[1.0.6]**:
* **Updated Ignore List**: Added `trustedadvisor` and `support` to the default organization-managed exclusion list.
* **Variable Persistence**: The script now includes services in the `service_linked_roles` list even if they have the `boc:created_by: terraform` tag. This ensures that a `terraform apply` doesn't attempt to delete existing managed roles just because they weren't part of the current "import" batch.
* **Logic Refinement**:
* **Ignore List**: If a service is in this list, it is excluded from **both** the `import.tf` and the `.tfvars`.
* **Terraform Tag**: If a service has the tag, it is excluded from the `import.tf` (to avoid state conflicts) but **included** in the `.tfvars` (to maintain the resource in the state).


### [1.0.5] - 2026-04-14
* **Added**: Built-in ignore list for organization-managed services (e.g., GuardDuty, Security Hub, SSO).
Expand Down Expand Up @@ -105,4 +113,3 @@ resource "aws_iam_service_linked_role" "roles" {

### [1.0.0] - 2026-04-14
- **Initial Release**: Basic SLR scraping and file generation.

46 changes: 25 additions & 21 deletions local-app/python-tools/aws-service-linked-roles/aws-slr-generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import csv
from botocore.exceptions import ClientError, ProfileNotFound

VERSION = "1.0.5"
VERSION = "1.0.6"

# Default list of organization-managed services to ignore
DEFAULT_IGNORE_SERVICES = [
"access-analyzer", "guardduty", "inspector2", "acm", "cloudtrail",
"compute-optimizer", "member.org.stacksets.cloudformation",
"config-multiaccountsetup", "fms", "ipam", "organizations",
"securityhub", "sso"
"securityhub", "sso", "trustedadvisor", "support"
]

def get_session_info(session):
Expand Down Expand Up @@ -51,10 +51,13 @@ def generate_slr_terraform():
account_id, partition = get_session_info(session)
ignored_services = load_ignored_services(args.organization_services)

service_names_to_import = []
# This list will contain ALL services that should be in the TF state,
# including those already managed by Terraform.
all_managed_service_names = []

import_content = [f"# Generated by aws-slr-generate v{VERSION}\n"]
slr_found_count = 0
imported_count = 0
imports_staged_count = 0

paginator = iam.get_paginator('list_roles')
for page in paginator.paginate(PathPrefix='/aws-service-role/'):
Expand All @@ -63,7 +66,7 @@ def generate_slr_terraform():
role_name = role['RoleName']
role_arn = role['Arn']

# 1. Check Trust Policy for Service Principal
# Extract Service Principal
trust_policy = role.get('AssumeRolePolicyDocument', {})
service_principals = []
for statement in trust_policy.get('Statement', []):
Expand All @@ -77,36 +80,37 @@ def generate_slr_terraform():
if not service_principals:
continue

# Using the first service principal as the primary key
primary_service = service_principals[0]

# 2. Check for Tags (boc:created_by == terraform)
# Check Tags
try:
tags_response = iam.list_role_tags(RoleName=role_name)
role_tags = {tag['Key']: tag['Value'] for tag in tags_response.get('Tags', [])}
except ClientError:
role_tags = {}

# 3. Filtering Logic
is_ignored = any(ignored in primary_service for ignored in ignored_services)
is_managed_by_tf = role_tags.get('boc:created_by') == 'terraform'

if is_ignored:
import_content.append(f"# Skipping {primary_service}: Organization-managed service\n")
elif is_managed_by_tf:
import_content.append(f"# Skipping {primary_service}: Role '{role_name}' already managed by terraform (tag detected)\n")
else:
import_content.append(f'import {{\n')
import_content.append(f' to = aws_iam_service_linked_role.roles["{primary_service}"]\n')
import_content.append(f' id = "{role_arn}"\n')
import_content.append(f'}}\n\n')
service_names_to_import.append(primary_service)
imported_count += 1
# If not in the ignore list, it BELONGS in the tfvars list
all_managed_service_names.append(primary_service)

if is_managed_by_tf:
import_content.append(f"# Skipping {primary_service}: Role '{role_name}' already managed by terraform (tag detected)\n")
else:
import_content.append(f'import {{\n')
import_content.append(f' to = aws_iam_service_linked_role.roles["{primary_service}"]\n')
import_content.append(f' id = "{role_arn}"\n')
import_content.append(f'}}\n\n')
imports_staged_count += 1

# Prepare Vars Content
service_names_to_import = sorted(list(set(service_names_to_import)))
# Prepare Vars Content (All non-ignored roles)
all_managed_service_names = sorted(list(set(all_managed_service_names)))
vars_content = [f"# Generated by aws-slr-generate v{VERSION}\n", "service_linked_roles = [\n"]
for service in service_names_to_import:
for service in all_managed_service_names:
vars_content.append(f' "{service}",\n')
vars_content.append("]\n")

Expand All @@ -127,8 +131,8 @@ def generate_slr_terraform():
print(f"AWS Partition: {partition}")
print(f"AWS Account: {account_id}")
print(f"Total SLRs Found: {slr_found_count}")
print(f"Imports Created: {imported_count}")
print(f"Ignored/Skipped: {slr_found_count - imported_count}")
print(f"Imports Staged: {imports_staged_count}")
print(f"Total in Vars: {len(all_managed_service_names)}")
print("-" * 45)

except (ProfileNotFound, ClientError) as e:
Expand Down

0 comments on commit b5e2a48

Please sign in to comment.