There are many SailPoint objects which grow very fast and we might be interested in how often they are growing. Examples can be like:
1. How many identities were on-boarded yearly or monthly
2. How many snapshots were created yearly or monthly or even daily.
We can view this with the help of a Quicklink and Workflow. The following example represents how the Identity object is growing monthly in year 2016.
If you click next, it will give you total number of identities created in 2016 in monthly basis like the following.
You can monitor more and it will give you the previous screen again.
Here is how it was done. The following Quicklink was created to launch the workflow. The quicklink is of Task category, you can make it a custom category. Please see Quicklinks.pdf for detail.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE QuickLink PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<QuickLink action="workflow" category="Tasks" messageKey="Object Growth" name="Object Growth">
<Attributes>
<Map>
<entry key="workflowName" value="Object Growth"/>
</Map>
</Attributes>
</QuickLink>
The above QuickLink launchs the following workflow named "Object Growth". The only thing you need to modify before importing the workflow is the IIQ installation year in your implementation. It's in line number 06 and currently having a value of 2014.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Workflow PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Workflow explicitTransitions="true" name="Object Growth">
<Variable initializer="true" input="true" name="trace"/>
<Variable initializer="true" input="true" name="transient"/>
<Variable initializer="2014" name="stringIIQInstallYear"/>
<Step icon="Approval" name="Generic Step" posX="297" posY="124">
<Approval name="Object Growth" owner="ref:launcher" return="objectName,stringYear,stringMonth" send="stringIIQInstallYear">
<Form name="Object Growth">
<Attributes>
<Map>
<entry key="pageTitle" value="Object Growth"/>
</Map>
</Attributes>
<Section columns="3" name="Configuration">
<Field displayName="Object" filterString="" name="objectName" postBack="true" required="true" type="string">
<AllowedValuesDefinition>
<Script>
<Source>
import java.util.List;
import java.util.ArrayList;
// CSV of all independent objects in 6.4
String classNames="AccountGroup,ActivityDataSource,Application,ApplicationActivity,ApplicationScorecard,AuditConfig,AuditEvent,AuthenticationQuestion,BatchRequest,Bundle,BundleArchive,Capability,Category,Certification,CertificationArchive,CertificationDefinition,CertificationGroup,Configuration,CorrelationConfig,Custom,DashboardContent,DashboardLayout,DatabaseVersion,Dictionary,DynamicScope,EmailTemplate,Form,FullTextIndex,GroupDefinition,GroupFactory,GroupIndex,Identity,IdentityArchive,IdentityDashboard,IdentityEntitlement,IdentityHistoryItem,IdentityRequest,IdentitySnapshot,IdentityTrigger,IntegrationConfig,JasperResult,JasperTemplate,LocalizedAttribute,ManagedAttribute,MessageTemplate,MiningConfig,MitigationExpiration,ObjectConfig,PasswordPolicy,Policy,PolicyViolation,Process,ProcessLog,Profile,ProvisioningRequest,QuickLink,Request,RequestDefinition,ResourceEvent,RightConfig,RoleChangeEvent,RoleIndex,RoleMetadata,RoleMiningResult,RoleScorecard,Rule,RuleRegistry,SPRight,Scope,ScoreConfig,Scorecard,Server,ServiceDefinition,ServiceStatus,SyslogEvent,Target,TargetAssociation,TargetSource,TaskDefinition,TaskResult,TaskSchedule,TimePeriod,UIConfig,UIPreferences,WorkItem,WorkItemArchive,Workflow,WorkflowCase,WorkflowRegistry,WorkflowTestSuite,Workgroup";
//In 7.0 QuickLinkOptions is an independent object, so it's added here for 7.0
//String classNames="AccountGroup,ActivityDataSource,Application,ApplicationActivity,ApplicationScorecard,AuditConfig,AuditEvent,AuthenticationQuestion,BatchRequest,Bundle,BundleArchive,Capability,Category,Certification,CertificationArchive,CertificationDefinition,CertificationGroup,Configuration,CorrelationConfig,Custom,DashboardContent,DashboardLayout,DatabaseVersion,Dictionary,DynamicScope,EmailTemplate,Form,FullTextIndex,GroupDefinition,GroupFactory,GroupIndex,Identity,IdentityArchive,IdentityDashboard,IdentityEntitlement,IdentityHistoryItem,IdentityRequest,IdentitySnapshot,IdentityTrigger,IntegrationConfig,JasperResult,JasperTemplate,LocalizedAttribute,ManagedAttribute,MessageTemplate,MiningConfig,MitigationExpiration,ObjectConfig,PasswordPolicy,Policy,PolicyViolation,Process,ProcessLog,Profile,ProvisioningRequest,QuickLink,QuickLinkOptions,Request,RequestDefinition,ResourceEvent,RightConfig,RoleChangeEvent,RoleIndex,RoleMetadata,RoleMiningResult,RoleScorecard,Rule,RuleRegistry,SPRight,Scope,ScoreConfig,Scorecard,Server,ServiceDefinition,ServiceStatus,SyslogEvent,Target,TargetAssociation,TargetSource,TaskDefinition,TaskResult,TaskSchedule,TimePeriod,UIConfig,UIPreferences,Widget,WorkItem,WorkItemArchive,Workflow,WorkflowCase,WorkflowRegistry,WorkflowTestSuite,Workgroup";
//Making array from CSV
String[] classes = classNames.split(",");
// defining a map to return the object name and counts;
List list=new ArrayList();
//iterating all objects
for(String clas: classes ){
list.add(clas);
}
return list;
</Source>
</Script>
</AllowedValuesDefinition>
</Field>
<Field displayName="Year" filterString="" name="stringYear" postBack="true" type="string">
<AllowedValuesDefinition>
<Script>
<Source>
import java.util.Calendar;
import java.util.List;
import java.util.ArrayList;
List list = new ArrayList();
int currentYear=Calendar.getInstance().get(Calendar.YEAR);
int intIiqInstallYear = Integer.parseInt(stringIIQInstallYear);
for (int i=intIiqInstallYear; i< currentYear+1; i++){
list.add(String.valueOf(i));
}
return list;
</Source>
</Script>
</AllowedValuesDefinition>
</Field>
<Field displayName="Month" filterString="" name="stringMonth" type="string">
<AllowedValuesDefinition>
<Script>
<Source>
import java.util.List;
import java.util.ArrayList;
List list = new ArrayList();
String monthNames="January,February,March,April,May,June,July,August,September,October,November,December";
String[] months = monthNames.split(",");
for(String month: months ){
list.add(month);
}
return list;
</Source>
</Script>
</AllowedValuesDefinition>
<Attributes>
<Map>
<entry key="hidden" value="script:"/>
<entry key="readOnly">
<value>
<Script>
<Source>
if(stringYear != null) return false;
else return true;
</Source>
</Script>
</value>
</entry>
</Map>
</Attributes>
</Field>
</Section>
<Button action="next" label="Next"/>
<Button action="cancel" label="Cancel"/>
</Form>
</Approval>
<Transition to="Build Form"/>
</Step>
<Step icon="Task" name="Build Form" posX="294" posY="15" resultVariable="viewForm">
<Script>
<Source>
import java.sql.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import sailpoint.object.Form.Button;
import sailpoint.object.Form.Section;
import sailpoint.object.*;
int getLastDayOfMonth(int year, int month){
month=month+1;
if(month==9 || month==4 || month==6 || month==11){
return 30;
}
else if(month==2){
if(year%4==0){
return 29;
}
else{
return 28;
}
}
else{
return 31;
}
}
int getObjectCount(String clas, Calendar startDate, Calendar endDate){
int count;
Class className;
List filters = new ArrayList();
if(clas.equals("Workgroup")){
filters.add(Filter.eq("workgroup", true));
className= Class.forName("sailpoint.object.Identity");
}
else{
className = Class.forName("sailpoint.object." + clas);
}
filters.add(Filter.ge("created", startDate.getTime()));
filters.add(Filter.le("created", endDate.getTime()));
Filter f=Filter.and(filters);
QueryOptions qo = new QueryOptions();
qo.addFilter(f);
qo.setIgnoreCase(true);
count=context.countObjects (className, qo);
return count;
}
String formatKey(int key){
if(key < 10){
return "0" + String.valueOf(key);
}
else{
return String.valueOf(key);
}
}
int getMonthNumber(String[] months, String monthName){
for(int i=0; i< months.length; i++){
if(monthName.equals(months[i])){
return i;
}
}
}
//**************Main method*********************
int currentYear=Calendar.getInstance().get(Calendar.YEAR);
int currentMonth=Calendar.getInstance().get(Calendar.MONTH);
int iiqInstallYear=Integer.parseInt(stringIIQInstallYear);
String monthNames="January,February,March,April,May,June,July,August,September,October,November,December";
String[] months = monthNames.split(",");
String subTitle="";
Calendar startDate;
Calendar endDate;
Calendar currentStartDate;
Calendar currentEndDate;
Map map=new TreeMap();
if(stringYear==null){
subTitle="From " + stringIIQInstallYear + " to " + String.valueOf(currentYear);
startDate = new GregorianCalendar(iiqInstallYear, 0, 1);
endDate = new GregorianCalendar(currentYear, 11, 31);
for(int indexYear=iiqInstallYear; indexYear<currentYear+1; indexYear++){
currentStartDate = new GregorianCalendar(indexYear, 0, 1);
currentEndDate = new GregorianCalendar(indexYear, 11, 31);
map.put(String.valueOf(indexYear), getObjectCount(objectName, currentStartDate, currentEndDate));
}
}
else{
int year=Integer.parseInt(stringYear);
if(stringMonth==null){
subTitle="From January, " + stringYear + " to December, " + stringYear;
startDate = new GregorianCalendar(year, 0, 1);
endDate = new GregorianCalendar(year, 11, 31);
for(int indexMonth=0; indexMonth <12; indexMonth++){
currentStartDate = new GregorianCalendar(year, indexMonth, 1);
currentEndDate = new GregorianCalendar(year, indexMonth, 31);
String key=stringYear + "-" + formatKey(indexMonth+1);
map.put(key, getObjectCount(objectName, currentStartDate, currentEndDate));
}
}
else{
int month=getMonthNumber(months, stringMonth);
subTitle="For " + stringMonth + ", " + stringYear;
startDate = new GregorianCalendar(year, month, 1);
int lastDayOfMonth=getLastDayOfMonth(year, month);
endDate = new GregorianCalendar(year, month, lastDayOfMonth);
for(int dayIndex=0; dayIndex < lastDayOfMonth; dayIndex++){
currentStartDate = new GregorianCalendar(year, month, dayIndex+1);
currentEndDate = new GregorianCalendar(year, month, dayIndex+2);
String key=stringYear + "-" + String.valueOf(formatKey(month+1)) + "-" + String.valueOf(formatKey(dayIndex+1));
map.put(key, getObjectCount(objectName, currentStartDate, currentEndDate));
}
}
}
viewForm = new Form();
viewForm.setTitle("Object Growth");
viewForm.setSubtitle(subTitle);
List buttons = new ArrayList();
Form.Button submitButton = new Button("Monitor more", Form.ACTION_NEXT);
buttons.add(submitButton);
Form.Button cancelButton = new Button("Done", Form.ACTION_CANCEL);
buttons.add(cancelButton);
viewForm.setButtons(buttons);
Form.Section viewSection = new Form.Section("Section 1");
viewSection.setLabel(objectName);
viewSection.setType("text");
viewSection.setColumns(9);
for(Map.Entry entry : map.entrySet()) {
Field field1 = new Field();
field1.setColumnSpan(1);
String key=(String)entry.getKey();
field1.setName(key);
field1.setDisplayName(key);
field1.setValue(String.valueOf(entry.getValue()));
field1.setDefaultValue(String.valueOf(entry.getValue()));
viewSection.add(field1);
}
viewForm.add(viewSection);
return viewForm;
</Source>
</Script>
<Transition to="displayForm"/>
</Step>
<Step icon="Approval" name="displayForm" posX="430" posY="19">
<Approval name="View growth" owner="ref:launcher" return="">
<Arg name="workItemForm" value="ref:viewForm"/>
</Approval>
<Transition to="Generic Step"/>
</Step>
<Step icon="Start" name="Start" posX="92" posY="127">
<Transition to="Generic Step"/>
</Step>
<Step icon="Stop" name="Stop" posX="664" posY="60"/>
</Workflow>
No, I did not, but suggested Tonmay to do so in my first comment.
menno.pieters, Thanks for your suggestion. I proposed a nice plugin. I'm sorry to say they never replied my email even if I followed up two times. May be they don't think my idea is an excellent one.
Hello,
Somewhat related, you may want to consider the KPI Plugin provided by SailPoint Professional Services.
More info about Professional Services extension is available at https://community.sailpoint.com/t5/Professional-Services-Extensions/ct-p/Services_Plugins
Sylvain