In this blog post, we tackle automating the SAS Intelligence Platform administrator’s task of replicating (cloning) metadata users’ groups and roles from one user to another.
As more people in an organization become new SAS users or move to new projects or teams, their data and applications access credentials need to be set up or modified in SAS Metadata. In this scenario, a typical request to SAS platform administrators looks like one of the following:
- Add Mr. A Smith as SAS user and make him a member of the groups and roles matching those of Ms. B. Brown.
- For an existing SAS user Mr. A. Smith, replace his memberships of the groups and roles to mimic those of Ms. B. Brown.
- For an existing SAS user Mr. A. Smith, add to his memberships of the groups and roles those that are missing from those of Ms. B. Brown.
SAS administrators routinely fulfill such requests by manually clicking through SAS Users plug-in in SAS Management Console. However, when a number of groups/roles to assign becomes rather large, this seemingly straightforward process can become overwhelming and error-prone.
Let's change the conventional practice of manually replicating group memberships from one user to another. Let's do it programmatically, which will make it easy, repeatable, fast, consistent, efficient and reliable.
Using SAS metadata functions to GET user-group associations
While you can employ macro %MDUEXTR to produce a list of groups for metadata users, here we are going to dive deeper and implement this task by using SAS data step metadata functions.
First, we connect to the SAS metadata server:
/* Connect to the SAS metadata server */ options metaserver = 'your_metadata_server' metaport = 8561 metarepository = Foundation metauser = 'sasadm@saspw' metapass = '{SAS002}encrypted_password' ; |
Then, let’s run the following code:
/* User Name (not Display Name) */ %let user_name = B Brown; data USER_GROUPS; length UserName UserURI GroupURI GroupName GroupDisplayName $256; call missing(of _char_); UserName = "&user_name"; UserURI = "omsobj:Person?@Name='&user_name'"; do j=1 by 1 while(metadata_getnasn(UserURI, 'IdentityGroups', j, GroupURI)>0); rc = metadata_getattr(GroupURI, 'Name', GroupName); rc = metadata_getattr(GroupURI, 'DisplayName', GroupDisplayName); output; end; run; |
Code highlights
In this code, we loop through user’s groups while the following condition is true:
metadata_getnasn(UserURI, 'IdentityGroups', j, GroupURI) > 0
It is important to realize that function METADATA_GETNASN returns two values. Besides its return value (number of associated objects) referenced by the function itself, its 4-th argument (GroupURI) also returns a value of the j-th associated object of the specified association ('IdentityGroups') for the specified object (UserURI). When j becomes greater than the number of groups associated with the user, this function returns value of -4 ( “j is out of range” ) which effectively stops further iterations of the do-loop.
Within the do-loop, we use function METADATA_GETATTR which returns the value (GroupName) of the specified attribute ('Name') for the specified object (GroupURI). Successful completion is indicated by its return value rc=0.
Similarly, the second call of the METADATA_GETATTR function returns GroupDisplayName.
Output
As a result, we are going to get the following USER_GROUPS table:
Using SAS metadata functions to SET user-group associations
Analogous to retrieving user groups associations using METADATA_GETNASN function, we can SET (or assign) such associations using METADATA_SETASSN function. For example, the following statement will modify existing list of members for the group referenced by the first argument (pguri) by adding a new user referenced by the 4-th argument (curi):
rc = metadata_setassn(pguri, 'MemberIdentities', 'Merge', curi);
SAS macro to automatically clone group memberships from one user to another
Now, let’s cut all the exploratory code and wrap everything together into a little nice macro:
%macro mm_clone_user_groups ( child_name=, /* Child's User Name (not a Display Name) */ parent_name=, /* Parent's User Name (not a Display Name) */ mod=ADD /* Modifier (optional, values – Add or Replace, default=Add */ ); data _null_; length curi cguri puri pguri $256; call missing(of _char_); curi = "omsobj:Person?@Name='&child_name'"; /* Child's URI */ puri = "omsobj:Person?@Name='&parent_name'"; /* Parent's URI */ /* Conditionally remove existing Child's Groups */ if upcase("&mod")="REPLACE" then /* Loop through Child's Groups */ do i=1 by 1 while(metadata_getnasn(curi, 'IdentityGroups', i, cguri)>0); /* Remove Child User from each Child's Group */ rc = metadata_setassn(cguri, 'MemberIdentities', 'Remove', curi); end; /* Loop through Parent's Groups */ do j=1 by 1 while(metadata_getnasn(puri, 'IdentityGroups', j, pguri)>0); /* Add Child User to each Parent's Group */ rc = metadata_setassn(pguri, 'MemberIdentities', 'Merge', curi); end; run; %mend mm_clone_user_groups; |
Code highlights
The first do-loop executes conditionally only when Modifier is REPLACE (case-insensitive). It iterates through all the groups that child user belongs to and remove that child user from each such a group. Please note that removing a user from a group’s “Current Members” list is equivalent to removing this group from the user’s “Member of” list.
Similarly, adding a group to a user’s “Member of” list is equivalent to adding this user to the group’s “Current Members” list. We use this fact in the second do-loop when for each group associated with the parent we add the child as the group member.
Unless we need to report on the groups we are “cloning” from the parent, we may skip capturing their attributes (like Name or Display Name) using METADATA_GETATTR function as we did in the previous section.
Macro usage
Here are a couple examples of the macro invocation:
Example 1
%mm_clone_user_groups(child_name=A. Smith, parent_name=B Brown, mod=replace)
In this example, the macro will remove all the child’s group associations and replace them by those of the parent user.
Example 2
%mm_clone_user_groups(child_name=A. Smith, parent_name=B Brown)
Here, by default mod=ADD and the macro will augment the child’s group associations by those of the parent user.
Note, that these two examples cover all three scenarios of the group membership cloning outlined in the beginning of this post.
Acknowledgement
Great thanks to Carl Sommer of SAS Technical Support for his invaluable assistance and guidance in navigating through the SAS metadata functions and metadata object associations.
Questions? Thoughts? Comments?
Do you find this blog post useful? How do you administer user group identities/memberships in SAS metadata? Do you have questions, concerns, suggestions, or comments? Please share with us below in the Comments section.
10 Comments
Hi,
macro works like a charm. Maybe it'd be useful to add some lines to check, whether child and parent user, respectively, exist at all in metadate.
Right now, the code runs without errors / warnings / notes, etc. even, if one of the two necessary users do not even exist!
Cheers,
FK1
Thank you, Felix, for your feedback. Much appreciated.
This is such a great solution to a common problem that many of us are familiar with. It's great to see that there is a way to automate this process and make it much easier for SAS administrators. Do you think this process could be used for other tasks as well?
Thank you, Jane, for your feedback. Yes, practically any SAS administration task that routinely performed manually through SAS Management Console interface can be automated using SAS metadata functions and procedures for metadata.
Another great post Leonid! Thank you for sharing!
You are welcome, Kirk! Thank you for your feedback.
Awesome, this blog is one of the best I have read on this topic.
Thank you, Laura, I appreciate your comment.
Hi Leonid,
Another great programmatic SAS metadata post!
If SAS admins are looking for a visual way to duplicate users, groups or roles, Metacoda software has this capability as Paul describes in his blog posts:
Duplicating or Copying SAS users - https://platformadmin.com/blogs/paul/2020/03/duplicating-copying-sas-users/
Duplicating or Copying SAS groups and roles - https://platformadmin.com/blogs/paul/2020/03/duplicating-copying-sas-groups-roles/
Cheers,
Michelle
Thank you, Michelle, for your feedback and sharing additional resources. Metacoda is a great tool for SAS administrators. By the way, I also referenced in the Additional Resources section Paul Homes' blog post Duplicating or Copying SAS Users which is somewhat similar to the functionality described in this blog post - replicating/cloning metadata users' group/roles memberships from one user to another.