Can you tell if any of the contents of SAS Viya folder have changed, even content within a subfolder? Can you tell if the permissions on a folder or something inside it have changed since you last looked at them?
Revision tracking for content and for permissions on content is not a feature of SAS Viya, but this post shows one way you can track such changes yourself, with a bit of coding. There are many situations where it might be valuable to a SAS Administrator to be able to see what content or permissions changed between two points in time.
The method proposed in this post starts with two scripts: listcontent.sh and listeffectivepermissions.sh which call commands in the SAS Viya CLI and a couple of pyviyatools. When run, they write out files with timestamps in their names, respectively detailing the content, and the permissions on content beneath a given SAS Viya folder. If you run either script more than once in the same environment at different times, the resulting CSV files can then be compared with a diff tool. The differences revealed by that comparison show what content changed, or what permissions on content changed between those two times.
Let's start by looking at the scripts and how to use them. There is some explanation about the tools used by the scripts and the history and context behind them at the end.
We will detect changes to content in a SAS Viya folder structure by comparing CSV files that list the content, because CSV files are easy to work with.
We will first create a CSV file listing content - folders and other objects like reports, SAS programs, model files etc. - under a top-level SAS Viya folder before we make a change. Each line includes a little contextual information about the content item on the line.
Run this all at once, to create a script called listcontent.sh in your user's home directory and make it executable. The code block below uses a linux heredoc to pass the script code to the tee command, and it is written out to the listcontent.sh file. Notice that ‘$’ symbols are escaped by being prefixed with a ‘\’ so that they are not resolved as the listcontent.sh file is being written, because we want them to be resolved later when the listcontent.sh file is run. The escape characters themselves are not written to the listcontent.sh file:
tee ~/listcontent.sh > /dev/null << EOF
#!/bin/sh
# ------------------------------------
# USAGE INSTRUCTIONS
# 1. Authenticate using the CLI
# 2. Execution:
# listcontent.sh [path]
# Configuration
pyviyatoolspath=/opt/pyviyatools
unformattedfile="/tmp/members_unformatted.csv"
formattedfile="/tmp/members_formatted.csv"
sortedfile="/tmp/members_\$(date +%Y-%m-%d.%H_%M_%S).csv"
# The folder path to analyse is passed in as an argument
fldrpath=\$1
# echo "Listing content of folder \$fldrpath"
# Get folder id and store in \$gcid. Store folder URI in \$gcuri
gcid=\$(sas-viya --output fulljson folders show --path \$fldrpath | jq -r ".id")
gcuri="/folders/folders/\$gcid"
# echo "The \$fldrpath folder ID is " \$gcid
# echo "The \$fldrpath folder URI is" \$gcuri
# List objects under that top-level folder, with their paths, and redirect the result to temp file
\${pyviyatoolspath}/listmemberswithpath.py -u \$gcuri -r > \${unformattedfile}
# Format and sort CSV file containing these objects
awk -F"," 'BEGIN { OFS = "," } {\$7=\$1\$3; print }' \${unformattedfile} > \${formattedfile}
sort --field-separator=',' -k7 \${formattedfile} > \${sortedfile}
# cleanup
rm \${unformattedfile} \${formattedfile}
echo "\${sortedfile}"
EOF
chmod u+x ~/listcontent.sh
Note: Edit the line: pyviyatoolspath=/opt/pyviyatools
so that it points to the file system path where you have the pyviyatools project.
First, login to the sas-viya CLI as a member of the SAS Administrators group, or as another user who has permission to see all of the content you plan to list and compare. In my environment, a user called sasadm is a SAS Administrator:
sas-viya auth login -u sasadm -p thisisnotreallythepassword
Getting the list of content is best done as a user who has permission to see all the content under that folder, because the list will not contain objects (folders, reports and other things) that the authenticated user doesn't have permission to see. The script can be run by any user - they don't need to be an administrator for it to work, but a user who is a member of the SAS Administrators custom group should have permission to see all content, so an administrator user is probably a good choice.
When you have successfully authenticated against the SAS Viya CLI (there are more secure ways to do it than the one above!), you are ready to run listcontent.sh. In my SAS Viya environment from one of our classes, there is some example content in a SAS Viya folder called /gelcontent, so I pass that path in as the parameter to listcontent.sh:
~/listcontent.sh /gelcontent
Expected output from the echo command is a filename, something like this:
/tmp/members_2025-07-16.11_48_54.csv
That file contains the formatted and sorted CSV data describing the content under the top-level SAS Viya folder you pass in as a parameter. For a convenient way to remember it later, we can do something like this:
content_before=$(~/listcontent.sh /gelcontent)
echo $content_before
This stores the filename in an environment variable. You can use cat to see the content of the file:
cat $content_before
..., or if you prefer you can open it in your favorite text editor. In my SAS Viya environment, the first part of the content of that file looks like this:
Partial listing of content under /gelcontent in my lab environment
Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.
That first CSV file will be the baseline in a text-based comparison with a similar file, generated either later on the same environment, or on another environment; a diff. The second CSV file might in turn become the baseline for comparison with a third such file, and so on. Each CSV file produced by listcontent.sh is a snapshot at the time it was created. It helps that the filename includes a date-time stamp.
Now make a change to your content, and then run listcontent.sh again. In my lab environment, I will make a new folder called Code, under the existing /gelcontent/GELCorp/Finance folder:
Finance folder before making any changes to its content
Creating the Finance Code folder
Click OK and the new Code folder is created.
Having made a change, run this:
content_after=$(~/listcontent.sh /gelcontent)
echo $content_after
This writes out another up-to-date list of the content to a new file, and outputs the filename as before. We capture that filename and save it in another environment variable, content_after.
I won't show the second file - it looks more or less the same as the first one. The second file is slightly different but it is difficult find the difference by eye. We have tools for that task.
For command-line text file comparison, I like icdiff (improved colored diff). The compare tools in Notepad++ and VS Code are both good if you want to use a graphical interface. I will use icdiff for these examples, but the general idea is similar with any of them - the graphical tools can even show which characters in a line changed and which did not.
I ran this:
icdiff $content_before $content_after
And the resulting colored diff output looks like this, with the 'content_before' file on the left, and 'content_after' on the right. The icdiff output includes a few lines before and after each change, and not necessarily the whole of both files being compared. The matching, unchanged lines either side of each detected change are shown in white text, side-by-side in both columns:
Content differences: matching lines are in white. Lines only found in the second file are in green. If there were any lines only found in the first file, those would be shown in red.
In the right column, you can see that the $content_after file includes a new line shown in green, with nothing opposite in the left column:
/gelcontent/GELCorp/Finance/,4da5e1ed-3453593a7e3,Code,child,,/folders/folders/68-baeb-08aae079bdf0,/gelcontent/GELCorp/
This line corresponds to the new Code folder I created under /gelcontent/GELCorp/Finance folder.
We did not delete, move or rename any content, so there are no lines in red on the left column with unmatched lines on the right. Try it yourself if you want to see how it looks. A few strings on the right are in blue in this screenshot - I am not sure why, they do not indicate a difference. I think it's an artifact from the SSH terminal, or something like that.
The CSV file lines output by listcontent.sh do not contain information about the permissions on the folder or content object they represent. To detect changes in permissions we will use use another, slightly more complex script which is based on listcontent.sh, but does include that information.
Run this all at once, to create a script called listeffectivepermissions.sh in your user's home directory and make it executable:
tee ~/listeffectivepermissions.sh > /dev/null << EOF
#!/bin/sh
# ------------------------------------
# USAGE INSTRUCTIONS
# 1. Authenticate using the CLI
# 2. Execution:
# listeffectivepermissions.sh [path]
# Configuration
pyviyatoolspath=/opt/pyviyatools
unformattedfile="/tmp/members_unformatted.csv"
formattedfile="/tmp/members_formatted.csv"
sortedfile="/tmp/members_\$(date +%Y-%m-%d.%H_%M_%S).csv"
effectiveaccessfile="/tmp/effectiveaccess_\$(date +%Y-%m-%d.%H_%M_%S).csv"
# The folder path to analyse is passed in as an argument
fldrpath=\$1
# echo "Listing content of folder \$fldrpath"
# Get folder id and store in \$gcid. Store folder URI in \$gcuri
gcid=\$(sas-viya --output fulljson folders show --path \$fldrpath | jq -r ".id")
gcuri="/folders/folders/\$gcid"
# echo "The \$fldrpath folder ID is " \$gcid
# echo "The \$fldrpath folder URI is" \$gcuri
# Step 1 - list objects under that top-level folder, with their paths, and redirect the result to temp file
\${pyviyatoolspath}/listmemberswithpath.py -u \$gcuri -r > \${unformattedfile}
# Format and sort CSV file containing these objects
awk -F"," 'BEGIN { OFS = "," } {\$7=\$1\$3; print }' \${unformattedfile} > \${formattedfile}
sort --field-separator=',' -k7 \${formattedfile} > \${sortedfile}
# Step 2 - loop through the list of object paths in the sortedfile, and run explainaccess.py for each object
# echo "Retrieving effective access for each object under \$fldrpath"
# echo "Headers: path,principal,read,update,delete,secure,add,remove"
rm -f \${effectiveaccessfile}
while IFS=',' read -r eapath eaid eaname eatype eadesc eauri eafullpath
do
\${pyviyatoolspath}/explainaccess.py -u \$eauri -p | sed 's,'"\$eauri"', '"\$eafullpath"'\,'"\$eauri"',g' >> \$effectiveaccessfile
done < "\$sortedfile"
# cleanup
rm \${unformattedfile} \${formattedfile}
echo "\$effectiveaccessfile"
EOF
chmod u+x ~/listeffectivepermissions.sh
Note: Edit the line: pyviyatoolspath=/opt/pyviyatools
so that it points to the file system path where you have the pyviyatools project.
Like listcontent.sh, this script get a list of all objects under the specified folder. But then, it goes through that list of objects (folders and content), and for each one it calls the pyviyatool explainaccess.py. This tool calls SAS Viya's /authorization/decision API, passing in parameters as needed, to get an explanation of whether each permission is granted or denied to each group or user who has any applicable (i.e. relevant) effective access control, either targeting that object directly or inherited down the content tree from a direct or indirect parent object. This endpoint is part of the Authorization service REST API that SAS Viya uses to make authorization decisions for whether a user can do something or not.
Create a timestamped file listing effective permissions on all folders and content under /gelcorp by running this:
permissions_before=$(~/listeffectivepermissions.sh /gelcontent)
echo $permissions_before
listeffectivepermissions.sh takes longer to run than listcontent.sh does for the same top-level folder, because it has more to do.
The expected output from the echo command is another filename, this time something like this:
/tmp/effectiveaccess_2025-07-15.13_19_10.csv
That file contains a detailed list of all the effective permissions for each relevant users and group that has any permissions granted or denied to it, on every object (folder and content) under the top-level SAS Viya folder you pass in as a parameter. It is significantly longer than the content list. Listing the file with cat will show that the lines it it look something like this (which is only a small part of the result file for that folder - this time the last few lines):
Effective access listing before making any changes
You may be able to see that each line names an object, with its URI, then a principal (a user or group), and then a comma separated list of authorization decisions for each of six permissions: read,update,delete,secure,add,remove. Principals are listed in the results only where there is some rule that directly or indirectly affects their access to the object. It does not clearly indicate when a principal is a user or a group - though it might be obvious to you from the name.
listeffectiveaccess.sh will show differences for added/removed/changed content as well as for added/removed/changed grants and denies of permissions, but because any content added or removed tends to have sets of effective permissions for several users or groups, each new object can result in several new lines, and conversely each removed object can result in several removed lines in the effective access results. To keep things simpler for this demonstration, I will not change any content, and will only change one permission.
I chose to edit authorization for the /gelcontent/Sales/Reports folder, granting a user named Santiago the update permission. He already has read permission on this folder, inherited from some other general authorization rule due to Santiago being a member of the Sales group.
Granting Santiago the Update permission on the Sales Reports folder
If you make a larger set of changes (which is more realistic), expect correspondingly more changes in the difference output.
Having made a change, you can run this:
permissions_after=$(~/listeffectivepermissions.sh /gelcontent)
echo $permissions_after
This writes out another up-to-date list of the effective permissions on content under /gelcontent to a new file, and outputs the filename as before. We capture that filename and save it in another environment variable, permissions_after.
I won't show the second file - it looks more or less the same as the first one.
If you are following my steps, run this to compare the CSV files listing effective permissions:
icdiff $permissions_before $permissions_after
The resulting diff output looks like this, with the 'permissions_before' file on the left, and 'permissions_after' on the right. As before the icdiff output includes a few lines before and after each change. Unchanged lines either side of each detected change are shown in white text, side-by-side in both columns:
Diff between effective access files created before and after a permission change
In the right column (for the effective permissions after the change) you can see a new line, for the user Santiago, which includes grant* in the authorization decision value which is for update (the authorization values are listed for six permissions in this order: read,update,delete,secure,add,remove. The asterisk (*) after the grant value indicates a directly-applied permission. Here's the line in full:
/gelcontent/GELCorp/Sales/Reports,/folders/folders/8b4a0bd4-9471-41e0-8d36-abcd4bf85a0d,Santiago,grant,grant*,prohibit,prohibit,prohibit,prohibit
It is saying that the second time I asked it to listed all effective permissions on all objects under /gelcontent, one of those effective permissions applied to the folder with a full path-name of /gelcontent/GELCorp/Sales/Reports, where the principal named Santiago (a user - here the line doesn't make it clear if Santiago is a user or a group name) is:
It is human readable, but it does take a little bit of effort to digest, especially when there are more changes. However, this output is CSV. There are other tools that are good for spotting differences in CSV output, and it could be imported into a world-leading best-in-class data analytics toolset if you happen to have one available. That would likely be excellent at summarizing differences and reporting on them.
These scripts were developed for a classroom situation, with small amounts of content and small changes to the content and its permissions. Our experience tells us that putting something like this into a production environment for larger scale, real-world use usually reveals some scalability or performance considerations, which once identified we can address.
For that reason, I would be very interested to talk to anyone who tries using these scripts to track changes in content or permissions in a real-world SAS Viya deployment. I suspect that when a large number of differences occur between two content or permission listings, the volume of the differences in the icdiff (or Notepad++ or VS Code compare views) may be hard to digest. Maybe we can summarize the number of changes at intermediate levels of the folder hierarchy to make it easier to see a bigger picture? I'm also interested in performance at scale - how long the scripts take to run and if they run successfully, in environments with more content and more changes than my lab/course environment. Are there techniques like paging through results from the API call, where there are too many to fetch in one go, that we need to incorporate in the scripts or the pyviyatools it calls? Perhaps there are optimizations we could make to make them perform better, for example by submitting a batch of objects to explain at once, rather than doing it one at a time? Let me know if you find any issues, or can suggest improvements to make.
The scripts should also work either as they are, or with very little modification, for a SAS Viya 3.x deployment. Feedback would be much appreciated if you try it.
I'm also interested in hearing from anyone who tries using SAS code (or another SAS tool) to load the CSV files listing content and effective permissions, and does any analysis or reporting on the data, especially the differences between sets of the data, as a method of tracking changes to content and permissions. If you try that, please do let us know how you get on.
As you've seen, the two main bash scripts in this post are run from a command line shell prompt (SSH session) on a Linux client machine. The additional tools used by these scripts to detect changes in content and permissions are:
I'm confident you could do something similar in PowerShell on Windows instead of in Bash if you want to. You can certainly Install and use the SAS Viya CLI on Windows, and Install and use the pyviyatools on Windows. If you are motivated enough to do that, you will have no trouble writing your own PowerShell script to replicate what I do in bash in this post. If you do, please share it so others can use it.
The pyviyatools project in GitHub includes many convenient tools/scripts to enable or simplify tasks in SAS Viya. It is documented here:
Install these tools, and make sure you can authenticate against your SAS Viya deployment as a SAS Administrator or another user with permission to see all the content in which you wish to detect changes.
See you next time!
Find more articles from SAS Global Enablement and Learning here.
Good news: We've extended SAS Hackathon registration until Sept. 12, so you still have time to be part of our biggest event yet – our five-year anniversary!
The rapid growth of AI technologies is driving an AI skills gap and demand for AI talent. Ready to grow your AI literacy? SAS offers free ways to get started for beginners, business leaders, and analytics professionals of all skill levels. Your future self will thank you.