Admin Alert: An Experimental Technique for Automatically Deleting User Profiles
April 19, 2006 Joe Hertvik
I have recently received several emails asking about the best way to automatically delete user profiles that haven’t been used for a specific number of days. This is a requirement for several reasons, including Sarbanes-Oxley compliance. While I’m not sure any process for the automatic deletion of user profiles is perfect, here’s how I would approach this issue by using a home-grown technique.
Please note that this technique is experimental and it is not bullet-proof. I offer it as an exercise to help you satisfy your own automatic deletion requirements. However, you should be extremely careful when putting an automatic deletion procedure into production, as any misstep could result in the mass deletion of a large number of user profiles on your system. If anyone reading this column discovers additional pitfalls or corrections to what is published here, please email them to me and I will publish an update in a later column.
The automatic deletion routine uses the ANZPRFACT command as its starting point to automatically disable any user profiles that are more than a set number of days old. Once the old profiles are identified, I then create a User Profile Information file, which contains information about all my user profiles. The information file can be read by a CL program that performs additional checks on the disabled profiles, and then automatically deletes any profiles that meet my specifications. This sounds simple, but there are several devils in the details. Here are the steps I would follow to bullet-proof this procedure as much as possible.
1. For the purpose of this article, let’s say that I want to automatically delete user profiles that are more than 120 days old.
2. At least once a week, I would run a Save Security Data command (SAVSECDTA) command that backs up my machine’s security data, including user profiles, private authorities, and configuration objects. A valid SAVSECDTA backup is important because if my routine accidentally deletes a needed user profile, I should be able to easily restore that profile from backup media without too much hassle. If I didn’t have this information and I needed to restore a profile that I accidentally deleted, I would be out of luck and the profile would need to be recreated from scratch. For more information about SAVSECDTA, see Dissecting an Option 21 Save.
3. Before setting up ANZPRFACT to automatically disable user profiles, I would inventory all my user profiles to determine which profiles I cannot afford to delete because they are necessary for system processing. In addition to IBM’s system profiles, which are user profiles that mostly begin with the letter ‘Q’ and which can not be disabled by ANZPRFACT processing, there are several other profiles that must never be disabled or deleted, including profiles that are created for several third-party packages. These profiles are required for i5/OS server jobs, and they must always be present or those jobs will not run. I would also want to protect any group user profiles that are used for authority checking but that never sign on to the system.
To ensure that my automated routines do not accidentally disable (and ultimately delete) any needed user profiles, I would add each of these protected profiles from my inventory to the system’s Active Profile List. User profiles listed on the Active Profile List are automatically skipped by the ANZPRFACT command as it performs its daily processing, and ANZPRFACT will never mark them as disabled.
Information on viewing and adding profiles to the Active Profile List can be found in an earlier article that I wrote called The Joys and Pains of Automatically Disabling User Profiles.
Besides adding protected user IDs to the Active Profile List, I will also want to ensure that I do not have any other non-group disabled profiles on my system that I want to keep, because the final step in this process, deleting disabled profiles, will remove all qualified disabled profiles from the system. So if I want to keep a user profile that is currently disabled, I would need to re-enable that profile and secure it in another manner.
4. After updating my Active Profile List, I would set up the ANZPRFACT command to automatically disable any profile that has been inactive for more than 120 days. I can do this by running the following ANZPRFACT command:
For more details on how ANZPRFACT works, check out the Disabling User Profiles article I mentioned above.
5. I would create a CL program to automatically delete any disabled user profiles on the system that are more than 120 days old. This program would run every night, and it executes the following code:
PGM DCL VAR(&DATE120) TYPE(*CHAR) LEN(6) DCLF FILE(QADSPUPB) /* Create a user profile information file for your + System */ DSPUSRPRF USRPRF(*ALL) TYPE(*BASIC) OUTPUT(*OUTFILE) + OUTFILE(QTEMP/QADSPUPB) /* Insert code here to calculate a date that is 120 days + earlier than the current date. Put the result in + the &DATE120 variable in YYMMDD format. This code is + not provided in this article */ OVRDBF FILE(QADSPUPB) TOFILE(QTEMP/QADSPUPB) + SECURE(*YES) /* Retrieve a record from the user information file */ GETREC: RCVF MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(ENDPGM)) /* Only review disabled user profiles */ IF COND(&UPSTAT *EQ '*DISABLED') THEN(DO) /* Skip processing system profiles that start with the + letter 'Q' */ IF COND(&UPUPRF *GT 'Q ') THEN(DO) IF COND(&UPUPRF *LT 'QZZZZZZZZZ') THEN(GOTO + CMDLBL(GETREC)) ENDDO /* Only delete non-group user profiles that are disabled + and that have not signed on for more than 120 days */ IF COND(&UPGID *NE 0) THEN(DO) IF COND(&UPPSOD *LE &DATE120) THEN(DLTUSRPRF + USRPRF(&UPUPRF) OWNOBJOPT(*CHGOWN QDFTOWN)) ENDDO ENDDO /* Get another record */ GOTO CMDLBL(GETREC) /* Clean up your processing and end the program */ ENDPGM: DSPJOBLOG OUTPUT(*PRINT) DLTOVR FILE(*ALL) ENDPGM
This code performs the following functions:
1. The Display User Profile command (DSPUSRPRF) creates a file of all user information on the system, including the names of each user, whether the profile is disabled or not, and the last sign-on date. This information will be written to the QADSPUPB file in the QTEMP library. QADSPUPB is based on the file formats found in the QADSPUPB file in the QSYS library, which the DSPUSRPRF command uses to format its output.
2. The program performs date processing to create a variable called &DATE120, which is a six-digit character variable containing a date in YYMMDD format that is exactly 120 days earlier than today’s date. This code is not provided in this article.
3. The program overrides the QADSPUPB file it was compiled under to the newly created QADSPUPB file in QTEMP, and it starts reading the file.
4. The program checks to see if the user profile is marked as disabled before continuing processing (field &UPSTAT is equal to ‘*DISABLED’). Because of this check, only those user profiles that were marked disabled by ANZPFRACT or by any other means are considered for deletion. This means that the ANZPFRACT pre-processing I perform each night thins the herd for potential 120-day deletion, and that this program will not consider deleting any active user profiles.
5. A check is performed to see if the user profile being evaluated starts with the letter ‘Q’ (field UPUPRF). If so, it is an IBM-supplied user profile and the program skips this record. This is a double check to ensure that no system profiles are accidentally deleted.
6. Another check is performed to see if the disabled user profile is a group profile for other users by using an IF statement to determine if it has a group ID number (field UPGID is not equal to 0). If it’s a group profile, it grabs another record for processing.
7. If the last sign-on date for the user is more than 120 days before the current date, the program deletes the user profile by using the Delete User Profile command (DLTUSRPRF).
I entered the 120-day check code to protect user profiles that are automatically disabled because of OS/400 security values that automatically disable user profiles after a set number of invalid sign-on attempts, as specified in the QMAXSIGN and QMAXSGNACN system values. These values can automatically disable a user profile if the user types in the wrong password several times in a row or if someone tries to hack the password. In that case, I would not want to delete the profile if it hasn’t yet reached its 120-day deletion limit. For more explanation on using QMAXSIGN and QMAXSGNACN, see Making OS/400 Profiles a Little More Secure.
Also notice that I changed the Owned Object Option parameter (OWNOBJOPT) of the DLTUSRPRF command to *CHGOWN QDFTOWN. This parameter tells i5/OS what to do if the user profile to be deleted owns any other objects on the system. By designating *CHGOWN QDFTOWN for this parameter, I am telling i5/OS to transfer ownership for any objects the deleted profile owns to the default owner for system objects, QDFTOWN. You can change the new object owner to any other system user you want, but if you do not specify a new owner, the DLTUSRPRF command will error out.
8. When the program reads the last record in the QADSPUPB file, it outputs its job log to a printer spool file, delete its overrides, and ends. The printer spool file can be printed to hard-copy and saved for later review, if any questions arise.
By running the routine this way, there are several checks to ensure that only those users who have not signed on for 120 days are deleted. It marks each profile not used in 120 days as disabled by using the ANZPFRACT command. Profiles that should be protected are listed in the Active Profile List and skipped in ANZPRFACT. Only users that are marked as disabled are considered for deletion. It contains a double check for protecting system profiles; it will not delete any group profiles; and it protects profiles that were accidentally disabled because of system security values. And it provides a print-out showing exactly which user profiles were deleted.
Still, there are enough potential issues with this technique that I still advise extreme caution before implementation. It’s easy enough to miscode the CL program running this routine or there could be some unknown issue I’ve overlooked as I planned the code. In addition, you may also want to read another article I wrote about the pitfalls of deleting user profiles and a follow-up article that expanded on the topic. There are a number of things to think about when creating a routine to automatically delete user profiles, and I urge caution in implementing this or any other approach, in order to ensure that you don’t accidental delete necessary system profiles.