Skip to main content

four_iam/resource/
role.rs

1use four::{
2    arn::Arn,
3    convert::{WillBe, WillMappable},
4    function::{
5        getatt::{Attribute, HaveAtt},
6        reference::{RefInner, Referenced},
7    },
8    logical_id::LogicalId,
9    service::IAM,
10    ManagedResource,
11};
12use serde::Serialize;
13
14use crate::property::{
15    action, policy_document::PolicyDocument, principal::Principal, statement::Statement,
16};
17
18#[derive(ManagedResource, Clone)]
19#[resource_type = "AWS::IAM::Role"]
20pub struct Role {
21    logical_id: LogicalId,
22    assume_role_policy_document: PolicyDocument,
23    description: Option<String>,
24    role_name: Option<WillBe<RoleName>>,
25    managed_policy_arns: Option<Vec<WillBe<Arn<IAM>>>>,
26}
27
28impl Role {
29    pub fn new(assume_role_policy_document: PolicyDocument, logical_id: LogicalId) -> Self {
30        Self {
31            logical_id,
32            assume_role_policy_document,
33            description: None,
34            role_name: None,
35            managed_policy_arns: None,
36        }
37    }
38
39    pub fn assume_role(id: LogicalId, principal: Principal) -> Self {
40        let statement = Statement::allow()
41            .action(vec![Box::new(action::sts::AssumeRole)])
42            .principal(principal);
43        let policy_document = PolicyDocument::latest(vec![statement]);
44
45        Self::new(policy_document, id)
46    }
47
48    pub fn description(mut self, description: &str) -> Self {
49        self.description = Some(description.to_string());
50        self
51    }
52
53    pub fn name(mut self, name: WillBe<String>) -> Self {
54        self.role_name = Some(name.map());
55        self
56    }
57
58    pub fn managed_policy_arns(mut self, arns: Vec<WillBe<Arn<IAM>>>) -> Self {
59        self.managed_policy_arns = Some(arns);
60        self
61    }
62}
63
64impl Referenced for Role {
65    type To = WillBe<RoleName>;
66
67    fn referenced(&self) -> RefInner {
68        RefInner::Id(self.logical_id.clone())
69    }
70}
71
72#[derive(Debug, Clone, Serialize)]
73pub struct RoleName(String);
74
75impl RoleName {
76    pub fn new(name: String) -> Self {
77        Self(name)
78    }
79}
80
81impl WillMappable<String> for RoleName {}
82
83#[derive(Debug, Clone, Serialize)]
84pub struct RoleArn(Arn<IAM>);
85
86impl From<Arn<IAM>> for RoleArn {
87    fn from(value: Arn<IAM>) -> Self {
88        RoleArn(value)
89    }
90}
91
92#[derive(Debug, Clone, Serialize)]
93pub struct RoleId(String);
94
95impl HaveAtt<RoleArn> for Role {}
96impl HaveAtt<RoleId> for Role {}
97
98impl Attribute for RoleArn {
99    fn name() -> &'static str {
100        "Arn"
101    }
102}
103
104impl Attribute for RoleId {
105    fn name() -> &'static str {
106        "RoleId"
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use crate::property::action;
114
115    #[test]
116    fn test_role1() {
117        let role_id = LogicalId::try_from("role-id").unwrap();
118        let statement = Statement::allow()
119            .action(vec![Box::new(action::sts::AssumeRole)])
120            .principal(Principal::from(ServicePrincipal::Lambda));
121        let assume_role_policy_document = AssumeRolePolicyDocument::latest(vec![statement]);
122        let role = Role::new(assume_role_policy_document, role_id);
123        let mut rhs = r#"{
124            "Type": "AWS::IAM::Role",
125            "Properties": {
126                "AssumeRolePolicyDocument": {
127                    "Version": "2012-10-17",
128                    "Statement": [
129                        {
130                            "Effect": "Allow",
131                            "Action": [ "sts:AssumeRole" ],
132                            "Principal": {
133                                "Service": [ "lambda.amazonaws.com" ]
134                            }
135                        }
136                    ]
137                }
138            }}"#
139        .to_string();
140        rhs.retain(|c| c != ' ' && c != '\n');
141        assert_eq!(serde_json::to_string(&role).unwrap(), rhs);
142    }
143}