-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
irsa create/update/delete role and attach policy
- Loading branch information
Showing
8 changed files
with
166 additions
and
300 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package aws | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"log" | ||
"slices" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go-v2/aws" | ||
"github.com/aws/aws-sdk-go-v2/service/iam" | ||
"github.com/aws/smithy-go" | ||
) | ||
|
||
// RoleManager represents the details needed to manage IAM roles | ||
type RoleManager struct { | ||
// RoleName represents the name of the IAM role | ||
RoleName string | ||
// Namespaces represents the list of namespaces associated with the role | ||
Namespaces []string | ||
// Policies represents the list of policies to be attached to the role | ||
Policies []string | ||
} | ||
|
||
func (r *RoleManager) PolicyArn(policy string) *string { | ||
prefix := "arn:aws:iam::" | ||
if strings.HasPrefix(policy, prefix) { | ||
return aws.String(policy) | ||
} | ||
return aws.String(fmt.Sprintf("%saws:policy/%s", prefix, policy)) | ||
} | ||
|
||
// DeleteIRSARole detaches specified policies from the IAM role and deletes the IAM role | ||
func (a *AwsIamClient) DeleteIRSARole(ctx context.Context, r RoleManager) error { | ||
for _, policy := range r.Policies { | ||
detachRolePolicyInput := &iam.DetachRolePolicyInput{ | ||
RoleName: aws.String(r.RoleName), | ||
PolicyArn: r.PolicyArn(policy), | ||
} | ||
_, err := a.Client.DetachRolePolicy(ctx, detachRolePolicyInput) | ||
// Ignore error if the policy is already detached or the role does not exist | ||
if errorHandle(err, []string{"NoSuchEntity"}) != nil { | ||
return err | ||
} | ||
log.Printf("Policy %s detached from role %s successfully", policy, r.RoleName) | ||
|
||
} | ||
input := &iam.DeleteRoleInput{RoleName: aws.String(r.RoleName)} | ||
_, err := a.Client.DeleteRole(ctx, input) | ||
// Ignore error if the role does not exist or there are other policies that this controller does not manage | ||
if errorHandle(err, []string{"DeleteConflict", "NoSuchEntity"}) != nil { | ||
return err | ||
} | ||
log.Printf("Role %s deleted successfully", r.RoleName) | ||
return nil | ||
} | ||
|
||
// CreateIRSARole creates an IAM role with the specified trust policy and attaches specified policies to it | ||
func (a *AwsIamClient) CreateIRSARole(ctx context.Context, accountId, issuerHostPath string, r RoleManager) error { | ||
providerArn := fmt.Sprintf("arn:aws:iam::%s:oidc-provider/%s", accountId, issuerHostPath) | ||
statement := make([]map[string]interface{}, len(r.Namespaces)) | ||
for i, ns := range r.Namespaces { | ||
statement[i] = map[string]interface{}{ | ||
"Effect": "Allow", | ||
"Principal": map[string]interface{}{ | ||
"Federated": providerArn, | ||
}, | ||
"Action": "sts:AssumeRoleWithWebIdentity", | ||
"Condition": map[string]interface{}{ | ||
"StringEquals": map[string]interface{}{ | ||
fmt.Sprintf("%s:sub", issuerHostPath): fmt.Sprintf("system:serviceaccount:%s:%s", ns, r.RoleName), | ||
}, | ||
}, | ||
} | ||
} | ||
trustPolicy := map[string]interface{}{ | ||
"Version": "2012-10-17", | ||
"Statement": statement, | ||
} | ||
trustPolicyJSON, err := json.Marshal(trustPolicy) | ||
if err != nil { | ||
return fmt.Errorf("failed to marshal trust policy: %w", err) | ||
} | ||
createRoleInput := &iam.CreateRoleInput{ | ||
RoleName: aws.String(r.RoleName), | ||
AssumeRolePolicyDocument: aws.String(string(trustPolicyJSON)), | ||
} | ||
|
||
_, err = a.Client.CreateRole(context.TODO(), createRoleInput) | ||
if errorHandle(err, []string{"EntityAlreadyExists"}) != nil { | ||
return err | ||
} | ||
log.Printf("Role %s created successfully", r.RoleName) | ||
|
||
updateRoleInput := &iam.UpdateAssumeRolePolicyInput{ | ||
RoleName: aws.String(r.RoleName), | ||
PolicyDocument: aws.String(string(trustPolicyJSON)), | ||
} | ||
|
||
_, err = a.Client.UpdateAssumeRolePolicy(context.TODO(), updateRoleInput) | ||
if err != nil { | ||
return fmt.Errorf("failed to update assume role policy for role %s: %w", r.RoleName, err) | ||
} | ||
log.Printf("Assume role policy for %s updated successfully", r.RoleName) | ||
|
||
for _, policy := range r.Policies { | ||
attachRolePolicyInput := &iam.AttachRolePolicyInput{ | ||
RoleName: aws.String(r.RoleName), | ||
PolicyArn: r.PolicyArn(policy), | ||
} | ||
|
||
_, err = a.Client.AttachRolePolicy(context.TODO(), attachRolePolicyInput) | ||
if err != nil { | ||
return err | ||
} | ||
log.Printf("Policy %s attached to role %s successfully", policy, r.RoleName) | ||
|
||
} | ||
return nil | ||
} | ||
|
||
// errorHandle handles specific errors by checking the error code against a list of codes to ignore | ||
func errorHandle(err error, errorCodes []string) error { | ||
if err != nil { | ||
var ae smithy.APIError | ||
if errors.As(err, &ae) && slices.Contains(errorCodes, ae.ErrorCode()) { | ||
fmt.Printf("Skipped error: %s \n", err.Error()) | ||
return nil | ||
} | ||
} | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.