forked from amazon-connect/amazon-connect-snippets
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sync_instances.py
executable file
·163 lines (137 loc) · 6.21 KB
/
sync_instances.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/env python3
import sys
import argparse
import datetime
from dateutil.tz import tzlocal
import boto3
import botocore
def parse_arn(arn):
# http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
elements = arn.split(':', 5)
result = {
'arn': elements[0],
'partition': elements[1],
'service': elements[2],
'region': elements[3],
'account': elements[4],
'resource': elements[5],
'resource_type': None
}
if '/' in result['resource']:
result['resource_type'], result['resource'] = result['resource'].split('/',1)
elif ':' in result['resource']:
result['resource_type'], result['resource'] = result['resource'].split(':',1)
return result
def assumed_role_session(role_arn, base_session):
base_session = base_session or boto3.session.Session()._session
fetcher = botocore.credentials.AssumeRoleCredentialFetcher(
client_creator=base_session.create_client,
source_credentials=base_session.get_credentials(),
role_arn=role_arn,
extra_args={
# feel free to change this name
'RoleSessionName': 'ConnectUserSync'
}
)
creds = botocore.credentials.DeferredRefreshableCredentials(
method='assume-role',
refresh_using=fetcher.fetch_credentials,
time_fetcher=lambda: datetime.datetime.now(tzlocal())
)
botocore_session = botocore.session.Session()
botocore_session._credentials = creds
return boto3.Session(botocore_session=botocore_session)
def get_users_by_username(client, instance_id):
users = {}
paginator = client.get_paginator("list_users")
page_iterator = paginator.paginate(InstanceId=instance_id)
for page in page_iterator:
for user in page["UserSummaryList"]:
users[user["Username"]] = user
return users
def get_deep_user(client, instance_id, user_id):
return client.describe_user(InstanceId=instance_id, UserId=user_id).get("User")
def index_profile(client, instance_id, call_name, list_name):
method = getattr(client, call_name)
profiles = method(InstanceId=instance_id)[list_name]
profiles_by_x = {}
for profile in profiles:
profiles_by_x[profile["Id"]] = profile
profiles_by_x[profile["Name"]] = profile
return profiles_by_x
def get_security_profiles_index(client, instance_id):
return index_profile(
client, instance_id, "list_security_profiles", "SecurityProfileSummaryList"
)
def get_routing_profiles_index(client, instance_id):
return index_profile(
client, instance_id, "list_routing_profiles", "RoutingProfileSummaryList"
)
def perform_sync(src, tgt, src_role=None, tgt_role=None, dry_run=False):
src_arn = parse_arn(src)
tgt_arn = parse_arn(tgt)
if src_role:
src_session = assumed_role_session(src_role).client("connect")
src_connect = src_session.client("connect", region_name=src_arn['region'])
else:
src_connect = boto3.client("connect", region_name=src_arn['region'])
if tgt_role:
tgt_session = assumed_role_session(tgt_role).client("connect")
tgt_connect = tgt_session.client("connect", region_name=tgt_arn['region'])
else:
tgt_connect = boto3.client("connect", region_name=tgt_arn['region'])
src_instance_id = src_arn['resource']
tgt_instance_id = tgt_arn['resource']
# Get Shallow Users
src_users = get_users_by_username(src_connect, src_instance_id)
tgt_users = get_users_by_username(tgt_connect, tgt_instance_id)
# Calculate difference between two sets:
diff_users = src_users.keys() - tgt_users.keys()
if len(diff_users) == 0:
print("Nothing to update, exiting...")
sys.exit(0)
print(f"There are {len(diff_users)} different users to update.")
deep_users = [
get_deep_user(src_connect, src_instance_id, src_users[user]["Id"])
for user in diff_users
]
# Get Mappings of Security and Routing Profiles
# Please note this only works if you have the same names
# for your security profiles between regions
# and those names are not in UUIDv4 format.
src_routing = get_routing_profiles_index(src_connect, src_instance_id)
tgt_routing = get_routing_profiles_index(tgt_connect, tgt_instance_id)
src_security = get_security_profiles_index(src_connect, src_instance_id)
tgt_security = get_security_profiles_index(tgt_connect, tgt_instance_id)
for user in deep_users:
security_profiles = []
for profile in user["SecurityProfileIds"]:
security_profiles.append(tgt_security[src_security[profile]["Name"]]["Id"])
routing_profiles = tgt_routing[src_routing[user["RoutingProfileId"]]["Name"]]["Id"]
if not dry_run:
resp = tgt_connect.create_user(
InstanceId=tgt_instance_id,
Username=user["Username"],
IdentityInfo=user["IdentityInfo"],
PhoneConfig=user["PhoneConfig"],
SecurityProfileIds=security_profiles,
RoutingProfileId=routing_profiles,
Tags=user["Tags"],
# Hopefully password not required, it depends on how your authentication was set up
# Password="D3l3tM3L8ter",
)
print(f"Added to {tgt_instance_id}: {user}")
if __name__ == "__main__":
example_arn1 = "arn:aws:connect:us-west-2:111122223333:instance/c2e9dc6f-3f69-40e2-b0ec-f78d0c62bee6"
example_arn2 = "arn:aws:connect:us-east-1:111122223333:instance/00000000-0000-0000-0000-000000000000"
parser = argparse.ArgumentParser(
description="sync user data from src instance to tgt instance",
epilog=f"Example command: ./sync_instances.py {example_arn1} {example_arn2}"
)
parser.add_argument("src", help="ARN of the source instance")
parser.add_argument("tgt", help="ARN of the target instance")
parser.add_argument("--src-role", help="role to assume in source account")
parser.add_argument("--tgt-role", help="role to assume in target account")
parser.add_argument("--dry-run", action="store_true", help="don't actually sync the users")
args = parser.parse_args()
perform_sync(args.src, args.tgt, args.src_role, args.tgt_role, args.dry_run)