@@ -33,19 +33,19 @@ def push_branch(branch_name: str):
3333 raise typer .Exit (1 )
3434
3535def detect_drift (local_doc , aws_doc ) -> bool :
36- """Detect if AWS permissions exist that are missing in local (removal drift )."""
37- local_statements = {json .dumps (stmt , sort_keys = True ) for stmt in local_doc .get ("Statement" , [])}
38- aws_statements = {json .dumps (stmt , sort_keys = True ) for stmt in aws_doc .get ("Statement" , [])}
36+ """Detect removal drift: AWS has permissions missing from local (danger )."""
37+ local_statements = {json .dumps (s , sort_keys = True ) for s in local_doc .get ("Statement" , [])}
38+ aws_statements = {json .dumps (s , sort_keys = True ) for s in aws_doc .get ("Statement" , [])}
3939
4040 missing_in_local = aws_statements - local_statements
4141
4242 if missing_in_local :
43- typer .echo ("❌ Drift detected: Your local policy is missing some permissions present in AWS." )
43+ typer .echo ("❌ Drift detected: Local is missing permissions present in AWS." )
4444 for stmt in missing_in_local :
4545 typer .echo (stmt )
4646 return True
4747
48- typer .echo ("✅ No drift detected (local may have extra permissions; that's fine)." )
48+ typer .echo ("✅ No removal drift detected (local may have extra permissions; that's fine)." )
4949 return False
5050
5151@app .command ()
@@ -57,6 +57,7 @@ def drift(
5757 approval_anyway : bool = typer .Option (False , "--approval-anyway" , help = "Request approval even if no drift" ),
5858 repo_full_name : str = typer .Option (None , "--repo" , help = "GitHub repo full name (e.g., org/repo)" )
5959):
60+ iam = boto3 .client ("iam" )
6061 if not account_id :
6162 account_id = boto3 .client ("sts" ).get_caller_identity ()["Account" ]
6263 policy_arn = f"arn:aws:iam::{ account_id } :policy/{ policy_name } "
@@ -69,13 +70,16 @@ def drift(
6970 raise typer .Exit (1 )
7071
7172 aws_doc = get_aws_policy_document (policy_arn )
72-
7373 drift_detected = detect_drift (local_doc , aws_doc )
7474
75- if not drift_detected and not approval_anyway :
76- typer .echo ("✅ No drift and no forced approval requested." )
77- raise typer .Exit ()
75+ if not drift_detected :
76+ _update_aws_policy (iam , policy_arn , local_doc )
77+ typer .echo (f"✅ AWS policy { policy_arn } updated to include any local additions." )
78+ if not approval_anyway :
79+ typer .echo ("✅ No forced approval requested. Exiting." )
80+ return
7881
82+ # From here, we need approval flow
7983 repo_full_name = repo_full_name or os .getenv ("GITHUB_REPOSITORY" )
8084 token = os .getenv ("GITHUB_TOKEN" )
8185
@@ -90,7 +94,6 @@ def drift(
9094 issue_num , _ = create_approval_issue (repo_full_name , token , policy_name , assignees = assignees )
9195
9296 choice = wait_for_sync_choice (repo_full_name , issue_num , token )
93- iam = boto3 .client ("iam" )
9497
9598 if choice == "local->aws" :
9699 merged_doc = merge_policy_documents (local_doc , aws_doc )
@@ -110,10 +113,10 @@ def drift(
110113 typer .echo ("⏭ No synchronization performed (skip)." )
111114
112115def _update_aws_policy (iam , policy_arn , policy_doc ):
113- versions = iam .list_policy_versions (PolicyArn = policy_arn )[' Versions' ]
116+ versions = iam .list_policy_versions (PolicyArn = policy_arn )[" Versions" ]
114117 if len (versions ) >= 5 :
115- oldest = sorted ((v for v in versions if not v [' IsDefaultVersion' ]), key = lambda v : v [' CreateDate' ])[0 ]
116- iam .delete_policy_version (PolicyArn = policy_arn , VersionId = oldest [' VersionId' ])
118+ oldest = sorted ((v for v in versions if not v [" IsDefaultVersion" ]), key = lambda v : v [" CreateDate" ])[0 ]
119+ iam .delete_policy_version (PolicyArn = policy_arn , VersionId = oldest [" VersionId" ])
117120 iam .create_policy_version (
118121 PolicyArn = policy_arn ,
119122 PolicyDocument = json .dumps (policy_doc ),
0 commit comments