This blog contains experience gained over the years of implementing (and de-implementing) large scale IT applications/software.

Report All Oracle User Accounts Through OEM Grid Control

You know the problem: You have Oracle Enterprise Manager Grid Control 10g installed and all your databases are configured in it.
It allows you to manage your entire landscape.  Great!

All of a sudden, a “leaver form” pops on your desk from HR with a long list of people who have left the company recently.
Do they have accounts in your Oracle databases?
If you have only one or two Oracle databases to check, then you can use the power of Grid Control to sort out the list in a few minutes.
What if you’ve got 50+ databases?
What if you’ve got 100+ databases?
Don’t they say: “The little jobs are the most time consuming”?

You may maintain an external spreadsheet of Oracle accounts, or you may not.  If you do, or even if you don’t, this article will show you how to configure a Oracle Enterprise Manager Grid Control 10g report that will show you all users accounts across the landscape at the click of a button.  You could use this technique to extend it to almost anything that can be done in an SQL or PL/SQL session.

Architecture overview:

We will be creating a new OEM Grid Control job called LIST_USER_ACOUNT_DETAILS, and a new report called LIST_USER_ACCOUNT_DETAILS.
The job is executed on the monitored database targets and it stores it’s output in the usual MGMT$ table in the OEM repository database.
The report simply pulls and re-connects the job output.

In OEM Grid Control create a new job called “LIST_USER_ACCOUNT_DETAILS”.
Add the database targets or target group.

Set the SQL script to be:

SET HEAD OFF;
SET PAGESIZE 9999;
SET LINESIZE 999;
SET TRIMOUT ON;
SET TRIMSPOOL ON;
SET FEEDBACK OFF;
SET COLSEP ' ';
COL MARKER FORMAT A2;
COL USERNAME FORMAT A30;
COL ACCOUNT_STATUS FORMAT A25;
COL LOCK_DATE FORMAT A12;
COL EXPIRY_DATE FORMAT A12;
COL HAS_REMOVED_ROLE FORMAT A30;
COL HAS_DBA_ROLE FORMAT A20;
WHENEVER SQLERROR EXIT FAILURE;
SELECT
'@|' marker,
username||'|' username,
account_status||'|' account_status,
'|'||TO_CHAR(lock_date,'DD-MM-RRRR')||'|' lock_date,
'|'||TO_CHAR(expiry_date,'DD-MM-RRRR')||'|' expiry_date,
'|'||DECODE((SELECT 'X' FROM dba_role_privs WHERE grantee = username AND granted_role ='REMOVED_ACCOUNTS'),'X','HAS ROLE "REMOVED_ACCOUNTS"','')||'|' has_removed_role,
'|'||DECODE((SELECT 'X' FROM dba_role_privs WHERE grantee = username AND granted_role ='DBA'),'X','HAS DBA ROLE!!','') has_dba_role
from dba_users;

Set the Parameters to be “-s” (silent):

Set the Credentials to use an account capable of querying DBA_USERS and DBA_ROLE_PRIVS (you could change this to use ALL_ views and use a lower priviledged account).
Set the schedule for the job. However frequently you wish to query the account details.

Submit the job for execution.

Once the job runs, the job output will be visible in the OEM MGMT$JOB_STEP_HISTORY table.
The actual SQLPlus output is stored in the OUTPUT column which is a CLOB field.
We have had to insert a marker in the SQL so that the output contains a ‘@|’ at the beginning of each line. This is used to determine the start of each record in the OUTPUT CLOB.
We just need to write a report to pull out the details.

Create a new report called “LIST_USER_ACCOUNT_DETAILS”.
On the “General” tab, select the “Use the specified target” option and enter the hostname of your OEM Grid Control repository server (DB server).
Select the “Run report using target privileges of the report owner (SYSMAN)” tick box.

On the “Elements” tab, add a “Table from SQL” item and then set the header, then paste in the statement below:

SELECT TARGET_NAME,JOB_TIME,ACCOUNT_DETAILS FROM (
SELECT '===============' TARGET_NAME,'================' JOB_TIME,'==================================================' ACCOUNT_DETAILS FROM DUAL
UNION
SELECT
TARGET_NAME,
TO_CHAR(END_TIME,'DD-MON-RRRR HH24:MI:SS') JOB_TIME,
-- non-regexp option -- TO_CHAR(REPLACE(SUBSTR(mh.OUTPUT,INSTR(mh.OUTPUT,'@| ',1,ct.x),INSTR(mh.OUTPUT,'@| ',1,ct.x+1) - INSTR(mh.OUTPUT,'@| ',1,ct.x)),'@| ','')) ACCOUNT_DETAILS
TO_CHAR(REGEXP_SUBSTR(mh.OUTPUT,'[^@]+', 1,ct.x)) ACCOUNT_DETAILS
FROM MGMT$JOB_STEP_HISTORY mh,
(SELECT rownum x FROM ALL_OBJECTS WHERE rownum<=5000) ct
WHERE TARGET_TYPE='oracle_database'
AND JOB_OWNER='SYSMAN'
AND JOB_NAME LIKE 'LIST_USER_ACCOUNT_DETAILS.%'
AND STEP_NAME='Command'
)
WHERE ACCOUNT_DETAILS IS NOT NULL
ORDER BY TARGET_NAME, JOB_TIME;

NOTE: In the report SQL, we have assumed a maximum number of 5000 user accounts across all database targets.
If you exceed this (if you have a lot of user accounts) then you will need to increase the number.
We have also assumed that SYSMAN is the owner of the job that created the output. You should change this if necessary.

You can choose to schedule the report if you wish (you should take into account the source job schedule).
It's probably best to secure the report so that only administrators can see it.

Test the report.

The output format consists of the following columns:
 TARGET NAME
 JOB TIME
 ACCOUNT DETAILS {
                 USERNAME
                 ACCOUNT STATUS
                 LOCK DATE
                 EXPIRY DATE
                 HAS REMOVED ROLE
                 HAS DBA ROLE
                }

The "HAS REMOVED ROLE" column relates to a "flag" role that I use to denote that an Oracle account has been de-activated purposely.
Instead of the account being deleted, it is retained for audit purposes and the REMOVED_ACCOUNTS role granted to it. You can choose to ignore or adapt this column.

Once you have the output, you can extract it to Excel for comparison with an Active Directory dump or any other means.


8 thoughts on Report All Oracle User Accounts Through OEM Grid Control

  1. Hi

    Very nice topics…but the very bad news about this is that we have to deal with credentials for hosts and databases accounts for targets.

  2. Hi,

    Yes that is very true.
    Credentials are a real pain in OEM.
    The way I manage this is by having one OEM account for running reports/backups etc.
    Against this account, all credentials are pre-stored.
    The OEM account password is then changed to a very secure password.

    For regular use, I ensure that I grant access to my regular user account for viewing job result output and administering databases without the credentials being stored.

    I find that this works best, as it means there's only one place to change SYSTEM passwords in OEM.

    Regards,

    Darryl

  3. A very useful exercise, however not perfect: one would miss last DB link in each DB due to lack of final marker. Here is how the job could be improved to overcome this issue:
    WHENEVER SQLERROR EXIT FAILURE;

    var users number;
    exec select count(1) into :users from dba_users;

    SET COLSEP ';'
    set echo off FEEDBACK OFF HEAD Off LINESIZE 999 NEWPAGE NONE PAGESIZE 99999 TRIMOUT ON TRIMSPOOL ON

    COL ACCOUNT_STATUS FORMAT A25
    COL EXPIRY_DATE FORMAT A12
    col external_name format a12
    COL MARKER FORMAT A2
    COL LOCK_DATE FORMAT A12
    COL USERNAME FORMAT A30

    — Add final marker, otherwise loosing last row!
    select us.*,decode(rownum,:users,'@|',null) from (
    select '@|' marker,:dbn db_name, u.*
    from dba_users u
    order by username
    ) us;

  4. Great thanks for the fix!
    I was slightly surprised that someone has looked at this. Can I ask, does it do what you need it to do?
    Back in 2011, we had Grid Control everywhere and most tools were not too hot on account discovery. So it made sense to implement a quick solution directly in Grid Control.
    Now there are a lot more security focused products on the market which may do a better job. But they will never be free, like this solution 😉

  5. Darryl – thanks.

    I found this searching for “MGMT$JOB_STEP_HISTORY” “OUTPUT” “CLOB” because there is no way I could have figured out the sql you did to pull out the clob results as separate rows of data.

    The EM library job SQL I ran was to calculate application schema sizes. Some databases had over 150 schemas. The job was executed against 400+ database targets.

    The query results from your query against MGMT$JOB_STEP_HISTORY (modified for what I needed) was over 8000 rows which imported perfectly into Excel / Sheets after running it from SQL Developer and exporting the data comma delimited from there.

    Really has come in handy. Thanks for showing us how to process MGMT$JOB_STEP_HISTORY.OUTPUT clob in a usable and valuable way.

    TR

  6. Is there an internal OEM view that has user account status? I see mgmt$db_users but it doesn’t have account status (ie. Expired, Locked, Open).

    1. Hello,

      I really don’t know.
      Maybe it will say in the internal table design schematics in the Oracle docs.
      I don’t have access to a Grid Control system to be able to check for you.

      All the best

      Darryl

Add Your Comment

* Indicates Required Field

Your email address will not be published.

*