AdhocAdam / smletsexchangeconnector

SMLets PowerShell based Exchange Connector for controlling Microsoft System Center Service Manager 2016+
https://adhocadam.github.io/smletsexchangeconnector/
GNU General Public License v3.0
29 stars 19 forks source link

Settings history #265

Closed PeterMiklian closed 3 years ago

PeterMiklian commented 3 years ago

I find it useful to have Settings history available. Everytime I click "OK" in SMLets Exchange Connector Settings GUI attribute diff would be writen to the log. Just as work and configuration items have their history visible in SCSM console and Cireson portal, too. Location could be SMLets Exchange Connector log.

AdhocAdam commented 3 years ago

I find it useful to have Settings history available.

Is there a particular setting or settings whose value you are changing where you would need to see this on a regular basis?

PeterMiklian commented 3 years ago

Is there a particular setting or settings whose value you are changing where you would need to see this on a regular basis?

Nothing specific, I'm just used to see this in Orchestrator runbook audit log, SCSM/Cireson item history etc. Use case: more SCSM admins, SMLets Exchange Connector stops working, I don't know why and after long debugging I find out that my admin colleague changed some setting causing issues. It was just a suggestion, no big deal Adam ;) If you or all connector users consider this useless, feel free to close this Feature request. Thank you.

AdhocAdam commented 3 years ago

Well I ask because of the myriad of settings in the connector (almost a reason on its own I suppose 🤔 ) if there was one or a few that were causing specific heartache because you didn't have a history of them. But the settings changing hands between admins makes sense to me.

That said, the good news is that history is already maintained via native SCSM/the EntityChangeLog table. So something tells me it's a matter of making the C#/SDK call, and then presenting it in the UI. I tried retrieving the history with SMLets Get-SCSMObjectHistory but the property names don't render. Although I have my guesses as to why.

In the interim, here's some alternative PowerShell using SMLets and the SDK that will fetch the history.

function Get-SMExcoHistory ($computername)
{
    $smletsSettings = Get-SCSMClass -name "SMLets.Exchange.Connector.AdminSettings$" -ComputerName $computername | Get-SCSMObject -ComputerName $computername
    $emg = New-Object Microsoft.EnterpriseManagement.EnterpriseManagementGroup $computername
    $history = $emg.EntityObjects.GetObjectHistoryTransactions($smletsSettings)
    $currentTimeZone = (Get-WmiObject win32_timezone).StandardName
    $timezone = [System.TimeZoneInfo]::FindSystemTimeZoneById($currentTimeZone)

    foreach ($entry in $history)
    {
        $historyObject = New-Object System.Object
        $historyObject | Add-Member -type "NoteProperty" -name "Property" -value $entry.ObjectHistory.Values.classhistory.PropertyChanges.Keys.Name
        $historyObject | Add-Member -type "NoteProperty" -name "Old Value" -value $entry.ObjectHistory.Values.classhistory.PropertyChanges.Values.First
        $historyObject | Add-Member -type "NoteProperty" -name "New Value" -value $entry.ObjectHistory.Values.classhistory.PropertyChanges.Values.Second
        $historyObject | Add-Member -type "NoteProperty" -name "User" -value $entry.UserName
        $historyObject | Add-Member -type "NoteProperty" -name "DateTime" -value ([System.TimeZoneInfo]::ConvertTimeFromUtc($entry.DateOccurred, $timezone))
        $historyObject
    }
}

Get-SMExcoHistory -computername "localhost"
PeterMiklian commented 3 years ago

Pulling history using PS didn't come to my mind, nice idea! I hope it will help more users investigating changes. It's not necessary to present history directly in GUI. Other places to consider for history changes storage: SMLets/Operations manager event log, separate *.log file, ... It would be easy to redirect output from fresh Get-SMExcoHistory function there after clicking "Save" in the settings GUI. What do you think?

AdhocAdam commented 3 years ago

Well a few things would have to happen there. Off the top of my head:

  1. The save mechanism in the UI, needs to trigger the History fetch after a Commit
  2. If it's being triggered by the UI (e.g. C#) that means the above PowerShell is scrapped entirely, re-written it in C#, and thus baked into the management pack
  3. If it's being triggered by the UI, there would need to be another setting to the effect of "define a history output" or something.
  4. This history file would have to be
    • created and potentially appended to manage/view the history over time
    • there wouldn't be a way to "protect" changes to the file. To your point of Admins changing settings, there isn't a way within the scope of this project to control changes to that file once it's been outputted
    • it's now a file that an admin has to manage and keep track of
  5. I suppose I can kinda see the SMLets Exchange Connector event log as a potential avenue here but I still wrestle with that given...

OR

Leverage the native SDK, retrieve the history, and then present it in the UI like everything else in SCSM. The history is auditable, unaltering, can't be tampered with, and behaves like everything else in SCSM with a History Tab. Plus it grooms like the rest of History in SCSM through the "Data Retention Setting."

Suffice it to say, the second option seems like a significantly lower bar to clear and in my opinion avoids a potential over/re-engineering of already present functionality seen throughout the SCSM SDK.

PeterMiklian commented 3 years ago

The second option definitely seems better. Let's put this on hold and give it more time maybe for feedback from more users. I can live without that, just wanted to put my idea into text :) Thanks.

AdhocAdam commented 3 years ago

I'm strongly considering moving forward with at least a first pass of built in History.

  1. I think the use case of multiple admins potentially shifting configuration is perfectly valid
  2. I think there is something to be said about Get-SCSMObjectHistory and its backing C# from SMLets as a starting point as seen below/here:
    public SCSMHistory(EnterpriseManagementObject emo)
    {
        History = new List<ObjectChange>();
        __HistoryData = new List<EnterpriseManagementObjectHistoryTransaction>();
        Instance = emo;
        foreach (EnterpriseManagementObjectHistoryTransaction ht in emo.ManagementGroup.EntityObjects.GetObjectHistoryTransactions(emo))
        {
            __HistoryData.Add(ht);
            ObjectChange pc = new ObjectChange();
            pc.LastModified = ht.DateOccurred.ToLocalTime();
            pc.UserName = ht.UserName;
            pc.Connector = ht.ConnectorDisplayName;
            bool addToHistory = false;
            foreach (KeyValuePair<Guid, EnterpriseManagementObjectHistory> h in ht.ObjectHistory)
            {
                foreach (EnterpriseManagementObjectClassHistory ch in h.Value.ClassHistory)
                {
                    foreach (KeyValuePair<ManagementPackProperty, Pair<EnterpriseManagementSimpleObject, EnterpriseManagementSimpleObject>> hpc in ch.PropertyChanges)
                    {
                        addToHistory = true;
                        pc.Changes.Add(new PropertyChange(Change.Property, ChangeType.Modify, hpc.Key.DisplayName, hpc.Value.First, hpc.Value.Second));
                    }
                }
                foreach (EnterpriseManagementObjectRelationshipHistory rh in h.Value.RelationshipHistory)
                {
                    addToHistory = true;
                    ManagementPackRelationship mpr = emo.ManagementGroup.EntityTypes.GetRelationshipClass(rh.ManagementPackRelationshipTypeId);
                    pc.Changes.Add(new PropertyChange(Change.Relationship, ChangeType.Modify, mpr.DisplayName, rh.SourceObjectId, rh.TargetObjectId));
                }

            }
            if (addToHistory) { History.Add(pc); }
        }
    }

This bit of code also confirms why I needed to use a custom History function above instead of SMLets native Get-SCSMObjectHistory. In my example I had to do

$entry.ObjectHistory.Values.classhistory.PropertyChanges.Keys.Name

Whereas SMLets/the backing C# in the example above uses

hpc.Key.DisplayName

The difference between the "Name" and "DisplayName" property comes down to the lack of DisplayString property to render the actual property (ElementID) to a friendly name as seen in the Management Pack's LanguagePacks section:

  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="SMLets.Exchange.Connector">
          <Name>SMLets Exchange Connector</Name>
          <Description>Provides centralized, persistent settings for the Powershell based SMLets Exchange Connector</Description>
        </DisplayString>
        <DisplayString ElementID="SMLets.Exchange.Connector.AdminSettings">
          <Name>SMLets Exchange Connector Settings</Name>
          <Description>Defines the settings for the open source SMLets based Exchange Connector</Description>
        </DisplayString>
        <DisplayString ElementID="SMLets.Exchange.Connector.AdminSettings.AdditionalMailbox">
          <Name>Additional Mailbox</Name>
          <Description>Defines additional mailboxes within the SMLets Exchange Connector</Description>
        </DisplayString>
        <DisplayString ElementID="SMLets.Exchange.Connector.AdminSettings.Edit">
          <Name>Edit</Name>
          <Description>View or edit the settings for the open source SMLets based Exchange Connector</Description>
        </DisplayString>
      </DisplayStrings>
      <KnowledgeArticles></KnowledgeArticles>
    </LanguagePack>
  </LanguagePacks>

This is something I would have never thought to do, because all of the properties in the connector are merely configuration/data points being set behind the scenes vs. being visualized to a user. Whereas the ones seen above are front and center from a usability/configuration standpoint as seen in the SCSM Console.

With all of that said, I think the other takeaway here is a potentially larger and more ambitious project around rendering the UI in a user's native language. The type of project that would absolutely demand community involvement and pull requests to achieve.

So for now - probably just a first pass at a History UI 😃

AdhocAdam commented 3 years ago

Completed pull request into dev. Will ship with next release via #275