tmaestrini / easyGovernance

governance and validation for configuration baselines in M365 – made as easy as possible
MIT License
11 stars 4 forks source link

Setting tenant config variables #2

Closed sympmarc closed 3 months ago

sympmarc commented 3 months ago

Before you can run Engine-BaselineValidator-M365-SPO.ps1, you need to set the tenant name for $AdminCenterURL. Once you do that, you get into a tangle when doing a push/pull.

This is something I used to do endlessly in my scripts. Now I have this scheme...

All I need to do is set the tenant in settings.json (below), and I'm off and running. Anywhere in my scripts I need a tenant URL, I can just use something like $dstSiteName = "https://$($tenant).sharepoint.com/sites/A".

Would something like this make sense here? Happy to do a PR with the scaffolding if so. I expect you plan multiple validators and baselines, so this would help to make things "run ready". Since you're using YAML for the validators, we could use YAML rather than JSON for the settings file, of course.

settings.json

{
    "tenant": "myTenantName"
}

In Engine-BaselineValidator-M365-SPO.ps1

Import-Module ./utilities/UtilityFunctions.psm1 -Force

Add-SympVariables

...

In UtilityFunctions.psm1

<#
.Synopsis
.DESCRIPTION
.EXAMPLE
   Add-SympVariables
#>
function Add-SympVariables {
   [CmdletBinding()]
   [Alias()]
   [OutputType([int])]
   Param
   (

   )

   Begin {

      $root = ".\utilities"
      $settings = Get-Content -Path "$($root)\.settings.json" | ConvertFrom-Json

   }
   Process {

      Write-Host "Setting global variables..."

      Set-Variable -Scope global -Name tenant -Value $settings.tenant
      Set-Variable -Scope global -Name adminSiteUrl -Value "https://$($tenant)-admin.sharepoint.com"      

   }
   End {

   }
}
tmaestrini commented 3 months ago

Great approach, @sympmarc! Thank you for this. As we look for implementing a multi tenant scenario, there will be a setup to store configuration for multiple dedicated tenants. Let's discuss and go for a solution based on your idea. We should implement it as a YAML file.

So, what about creating a generic template (template.eps) that contains the outline of a tenant configuration -- and which is populated with tenant specific data during a setup flow ({tenantname}.yml)?

tmaestrini commented 3 months ago

I will dig into later on...

sympmarc commented 3 months ago

So, what about creating a generic template (template.eps) that contains the outline of a tenant configuration -- and which is populated with tenant specific data during a setup flow ({tenantname}.yml)?

In my PR, I included .settings_template.json, intending that to be the "template" for the tenant settings.

I figure you'll want to change what's in my PR (if you reject it, I won't be offended) to fit your patterns, but by setting up a PSM1 file with the functions, we can expand the modularity of things, etc.

tmaestrini commented 3 months ago

I really like your suggestions in #3! Let's start with your implementation and extend it afterwords (step by step):

  1. Modularization
  2. Refine settings.json structure (and change to settings.yml) that even contains all baseline configs of the tenant / customer
  3. Refine modules structure (according to PoSh module recommendations)
  4. Implement proper validation workflow based on template
sympmarc commented 3 months ago

I just converted from JSON to YAML in my PR.

tmaestrini commented 3 months ago

What do you think about this: When we're going to refine the "modularization", shouldn't we go for this structure:

— src
  — Private // contains all modules that are not shared "outside" the solution
    HelperModule1.ps1
    HelperModule2.ps1
    ....ps1
  — Public // contains all modules that are accessible ("workflows")
    Start-Validation.ps1
    Invoke-Baselines.ps1
    ....ps1
  Validators.psm1 // integrates all validation modules
  Baselines.psm1 // integrates all baseline modules
— templates // the settings for tenant configs
  tenant1.yml
  tenant2.yml
  tenant3.yml
sympmarc commented 3 months ago

Now you're getting fancy. :) You probably have thought further ahead than I have. I like where you're aiming, though.

sympmarc commented 3 months ago

Should the baselines be in a PSM1 file, though? Shouldn't they stand alone as YAML files so people can choose from the baseline they want to use?

Also, I'm not sure how you see the multi-tenant aspect of this working, but it seems like it would be good to optimize for a single tenant as a start.

tmaestrini commented 3 months ago

Should the baselines be in a PSM1 file, though? Shouldn't they stand alone as YAML files so people can choose from the baseline they want to use?

Exactly. In my thoughts, Baselines.psm1 only imports the necessary modules for baseline management – as an example:

$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )

Foreach ($import in @($Public + $Private)) {
  Try {
    . $import.fullname
  }
  Catch {
    Write-Error -Message "Failed to import function $( $import.fullname ): $_"
  }
}

Export-ModuleMember -Function $Public.Basename

As you say, the baseline definition remains in its own yaml-file, e.g.baselines/M365.SPO-5.2.yml The settings file (tenant.yml) contains a reference to the baselines which are linked to the tenant:

Tenant: sympraxis  # <-- I like it BTW 🤣

Baselines:
  - M365.SPO
  - M365.<Baseline1>
  - M365.<Baseline2>
  - M365.<Baseline...>

With this approach, you also can take the settings.yml and put it in a repo – as you then have your customers' setup documented "for free"😃

sympmarc commented 3 months ago

BTW, I didn't intend for .settings.yml to end up in the repo. I meant it to be excluded by .gitignore.

tmaestrini commented 3 months ago

Yes, I know. I've understood your idea! We should then exclude the whole 'tenants' folder...

tmaestrini commented 3 months ago

I've pushed a bunch of changes on an experimental branch – containing a small architectural extension: https://github.com/tmaestrini/easyGovernance/tree/experimental

What do you think about? If you agree with my extension, I would merge them into develop and main branch. See the example in the examples folder (or also on the bottom of the README) – running a validation becomes super easy! 😃

sympmarc commented 3 months ago

What you've done makes sense to me. It'll give us good modularity to move forward with additional approaches. Go for it!