Skip to main content

08_tenant_migration/
08_tenant_migration.rs

1//! Tenant Migration
2//!
3//! This example demonstrates:
4//! - Moving resources from one tenant to another
5//! - Re-creating resources with new tenant context
6//! - Updating resource references after migration
7//!
8//! Scenario: Migrating a user and their resources from old-tenant to new-tenant.
9//!
10//! Run with: `cargo run --example 08_tenant_migration`
11
12use std::sync::Arc;
13use tokio::sync::RwLock;
14use wami::arn::{TenantPath, WamiArn};
15use wami::context::WamiContext;
16use wami::service::{GroupService, TenantService, UserService};
17use wami::store::memory::InMemoryWamiStore;
18use wami::wami::identity::group::requests::CreateGroupRequest;
19use wami::wami::identity::user::requests::{CreateUserRequest, ListUsersRequest};
20use wami::wami::tenant::model::TenantId;
21
22#[tokio::main]
23async fn main() -> Result<(), Box<dyn std::error::Error>> {
24    println!("=== Tenant Migration ===\n");
25
26    let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));
27
28    // Create root context
29    let root_context = WamiContext::builder()
30        .instance_id("123456789012")
31        .tenant_path(TenantPath::single(0))
32        .caller_arn(
33            WamiArn::builder()
34                .service(wami::arn::Service::Iam)
35                .tenant_path(TenantPath::single(0))
36                .wami_instance("123456789012")
37                .resource("user", "admin")
38                .build()?,
39        )
40        .is_root(true)
41        .build()?;
42
43    // Create old tenant context
44    let old_tenant_context = WamiContext::builder()
45        .instance_id("123456789012")
46        .tenant_path(TenantPath::single(80000000))
47        .caller_arn(
48            WamiArn::builder()
49                .service(wami::arn::Service::Iam)
50                .tenant_path(TenantPath::single(80000000))
51                .wami_instance("123456789012")
52                .resource("user", "admin")
53                .build()?,
54        )
55        .is_root(false)
56        .build()?;
57
58    // Create new tenant context
59    let new_tenant_context = WamiContext::builder()
60        .instance_id("123456789012")
61        .tenant_path(TenantPath::single(90000000))
62        .caller_arn(
63            WamiArn::builder()
64                .service(wami::arn::Service::Iam)
65                .tenant_path(TenantPath::single(90000000))
66                .wami_instance("123456789012")
67                .resource("user", "admin")
68                .build()?,
69        )
70        .is_root(false)
71        .build()?;
72
73    // === CREATE TENANTS ===
74    println!("Step 1: Creating source and destination tenants...\n");
75
76    let tenant_service = TenantService::new(store.clone());
77
78    let _old_tenant_id = TenantId::from_string("80000000").unwrap();
79    tenant_service
80        .create_tenant(
81            &root_context,
82            "old-tenant".to_string(),
83            Some("Old Tenant (deprecated)".to_string()),
84            None,
85        )
86        .await?;
87    println!("✓ Created source tenant: old-tenant");
88
89    let _new_tenant_id = TenantId::from_string("90000000").unwrap();
90    tenant_service
91        .create_tenant(
92            &root_context,
93            "new-tenant".to_string(),
94            Some("New Tenant (target)".to_string()),
95            None,
96        )
97        .await?;
98    println!("✓ Created destination tenant: new-tenant");
99
100    // === CREATE RESOURCES IN OLD TENANT ===
101    println!("\nStep 2: Creating resources in old tenant...\n");
102
103    let user_service = UserService::new(store.clone());
104    let group_service = GroupService::new(store.clone());
105
106    // Create user
107    let user_req = CreateUserRequest {
108        user_name: "bob".to_string(),
109        path: Some("/users/".to_string()),
110        permissions_boundary: None,
111        tags: None,
112    };
113    let old_user = user_service
114        .create_user(&old_tenant_context, user_req)
115        .await?;
116    println!("✓ Created user in old-tenant:");
117    println!("  - Name: {}", old_user.user_name);
118    println!("  - ARN: {}", old_user.arn);
119
120    // Create group
121    let group_req = CreateGroupRequest {
122        group_name: "developers".to_string(),
123        path: Some("/groups/".to_string()),
124        tags: None,
125    };
126    let old_group = group_service
127        .create_group(&old_tenant_context, group_req)
128        .await?;
129    println!("\n✓ Created group in old-tenant:");
130    println!("  - Name: {}", old_group.group_name);
131    println!("  - ARN: {}", old_group.arn);
132
133    // Add user to group
134    group_service
135        .add_user_to_group(&old_tenant_context, "developers", "bob")
136        .await?;
137    println!("\n✓ Added bob to developers group in old-tenant");
138
139    // === MIGRATE TO NEW TENANT ===
140    println!("\n\nStep 3: Migrating resources to new tenant...\n");
141
142    // Re-create user in new tenant
143    println!("Migrating user...");
144    let new_user_req = CreateUserRequest {
145        user_name: old_user.user_name.clone(),
146        path: Some(old_user.path.clone()),
147        permissions_boundary: old_user.permissions_boundary.clone(),
148        tags: Some(old_user.tags.clone()),
149    };
150    let new_user = user_service
151        .create_user(&new_tenant_context, new_user_req)
152        .await?;
153    println!("✓ Re-created user in new-tenant:");
154    println!("  - Old ARN: {}", old_user.arn);
155    println!("  - New ARN: {}", new_user.arn);
156
157    // Re-create group in new tenant
158    println!("\nMigrating group...");
159    let new_group_req = CreateGroupRequest {
160        group_name: old_group.group_name.clone(),
161        path: Some(old_group.path.clone()),
162        tags: Some(old_group.tags.clone()),
163    };
164    let new_group = group_service
165        .create_group(&new_tenant_context, new_group_req)
166        .await?;
167    println!("✓ Re-created group in new-tenant:");
168    println!("  - Old ARN: {}", old_group.arn);
169    println!("  - New ARN: {}", new_group.arn);
170
171    // Re-establish group membership
172    println!("\nRestoring group membership...");
173    group_service
174        .add_user_to_group(&new_tenant_context, "developers", "bob")
175        .await?;
176    println!("✓ Re-added bob to developers group in new-tenant");
177
178    // === CLEANUP OLD TENANT (Optional) ===
179    println!("\n\nStep 4: Cleaning up old tenant (optional)...\n");
180
181    println!("In production, you would:");
182    println!("- Remove user from old group");
183    println!("- Delete user from old tenant");
184    println!("- Delete group from old tenant");
185    println!("- Audit all resource references");
186    println!("- Update application configurations");
187
188    // Example cleanup (commented to preserve state for demonstration)
189    // old_group_service.remove_user_from_group(&old_tenant_context, "developers", "bob").await?;
190    // old_user_service.delete_user("bob").await?;
191    // old_group_service.delete_group(&old_tenant_context, "developers").await?;
192    println!("\n(Cleanup skipped for demonstration purposes)");
193
194    // === VERIFICATION ===
195    println!("\n\nStep 5: Verifying migration...\n");
196
197    let (old_users, _, _) = user_service
198        .list_users(
199            &old_tenant_context,
200            ListUsersRequest {
201                path_prefix: Some("/users/".to_string()),
202                pagination: None,
203            },
204        )
205        .await?;
206    println!("Users remaining in old-tenant: {}", old_users.len());
207
208    let (new_users, _, _) = user_service
209        .list_users(
210            &new_tenant_context,
211            ListUsersRequest {
212                path_prefix: Some("/users/".to_string()),
213                pagination: None,
214            },
215        )
216        .await?;
217    println!("Users now in new-tenant: {}", new_users.len());
218    for user in &new_users {
219        println!("  - {}", user.user_name);
220    }
221
222    println!("\n✅ Example completed successfully!");
223    println!("Key takeaways:");
224    println!("- Tenant migration requires re-creating resources in the target tenant");
225    println!("- ARNs change when resources move between tenants");
226    println!("- Preserve metadata (tags, paths) during migration");
227    println!("- Update all references after migration");
228    println!("- Consider phased migration for large-scale moves");
229
230    Ok(())
231}