Skip to content

Commit

Permalink
feat: set environment variables (#50)
Browse files Browse the repository at this point in the history
a mechanism to set Power Apps environment variables after the import has completed
  • Loading branch information
tdashworth authored Apr 16, 2021
1 parent abf5157 commit b052665
Show file tree
Hide file tree
Showing 12 changed files with 526 additions and 42 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
# SA1633: File should have header
dotnet_diagnostic.SA1633.severity = none

# SA1124: Do not use regions
dotnet_diagnostic.SA1124.severity = none

[tests/**/*.cs]

# SA1600: Elements should be documented
Expand Down
46 changes: 38 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ This project's aim is to build a powerful base Package Deployer template that si
- [Set SDK step states](#Set-sdk-step-states)
- [Connection references](#Connection-references)
- [Set connection references](#Set-connection-references)
- [Environment variables](#Environment-variables)
- [Set environment variables](#Set-environment-variables)
- [Data](#Data)
- [Import data](#Import-data)
- [Word templates](#Word-templates)
Expand Down Expand Up @@ -118,16 +120,16 @@ Environment variables must be prefixed with `PACKAGEDEPLOYER_SETTINGS_CONNREF_`

```powershell
$env:PACKAGEDEPLOYER_SETTINGS_CONNREF_DEVHUB_SHAREDVISUALSTUDIOTEAMSERVICES_CA653 = "shared-visualstudiot-44dd3131-3292-482a-9ec3-32cd7f3e799b"
Import-CrmPackage [...]
```

**Runtime setting**

```powershell
$runtimeSettings = @{
"ConnRef:devhub_sharedvisualstudioteamservices_ca653" = "shared-visualstudiot-44dd3131-3292-482a-9ec3-32cd7f3e799b"
}
$runtimeSettings = "ConnRef:devhub_sharedvisualstudioteamservices_ca653=shared-visualstudiot-44dd3131-3292-482a-9ec3-32cd7f3e799b"
Import-CrmPackage –CrmConnection $conn –PackageDirectory $packageDir –PackageName Package.dll –RuntimePackageSettings $runtimeSettings
Import-CrmPackage [...] –RuntimePackageSettings $runtimeSettings
```

The runtime setting takes precedence if both an environment variable and runtime setting are found for the same connection reference.
Expand All @@ -136,6 +138,34 @@ To get your flow connection names, go to your environment and navigate to _Data

As above, you will need to pass a licensed user's email via runtime settings or environment variables if the Package Deployer is not running in the context of a licensed user. In addition, **the connections passed in need to be owned by the user doing the deployment or impersonated by the deployment**.

### Environment variables

#### Set environment variables

You can set Power App environment variables either through system environment variables (for example, those [exposed on Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#access-variables-through-the-environment) from your variables or variable groups) or through Package Deployer [runtime settings](https://docs.microsoft.com/en-us/power-platform/admin/deploy-packages-using-package-deployer-windows-powershell#use-the-cmdlet-to-deploy-packages).

Environment variables must be prefixed with `PACKAGEDEPLOYER_SETTINGS_ENVVAR_` and followed by the schema name. Similarly, runtime settings must be prefixed with `EnvVar:` and followed by the environment variable schema name. For example, if an enviroment variable schema name was `pdt_testvariable`, this could be set via either of the following:

**Environment variable**

```powershell
$env:PACKAGEDEPLOYER_SETTINGS_ENVVAR_PDT_TESTVARIABLE = "test_value"
Import-CrmPackage [...]
```

**Runtime setting**

```powershell
$runtimeSettings = "EnvVar:pdt_testvariable=test_value"
Import-CrmPackage [...] –RuntimePackageSettings $runtimeSettings
```

The runtime setting takes precedence if both an environment variable and runtime setting are found for the same Power App environment variable.

If a value has already been set in the target environment then it will be overridden, otherwise a new Environment Variable Value will be created ad related to the Environment Variable Definition determined by the given schema name.

### Data

#### Import data
Expand Down Expand Up @@ -211,16 +241,16 @@ Environment variables must be prefixed with `PACKAGEDEPLOYER_SETTINGS_MAILBOX_`

```powershell
$env:[email protected] = "[email protected]"
Import-CrmPackage [...]
```

**Runtime setting**

```powershell
$runtimeSettings = @{
"Mailbox:[email protected]" = "[email protected]"
}
$runtimeSettings = "Mailbox:[email protected][email protected]"
Import-CrmPackage –CrmConnection $conn –PackageDirectory $packageDir –PackageName Package.dll –RuntimePackageSettings $runtimeSettings
Import-CrmPackage [...] –RuntimePackageSettings $runtimeSettings
```

The runtime setting takes precedence if both an environment variable and runtime setting are found for the same shared mailbox.
Expand Down
56 changes: 55 additions & 1 deletion src/Capgemini.PowerApps.PackageDeployerTemplate/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public static class Settings
/// </summary>
public const string ConnectionReferencePrefix = "ConnRef";

/// <summary>
/// The prefix for all Power Apps environment variables.
/// </summary>
public const string PowerAppsEnvironmentVariablePrefix = "EnvVar";

/// <summary>
/// The prefix for all environment variables.
/// </summary>
Expand Down Expand Up @@ -455,5 +460,54 @@ public static class Fields
public const string AzureActiveDirectoryObjectId = "azureactivedirectoryobjectid";
}
}

/// <summary>
/// Constants relating to the environmentvariabledefinition entity.
/// </summary>
public static class EnvironmentVariableDefinition
{
/// <summary>
/// The logical name.
/// </summary>
public const string LogicalName = "environmentvariabledefinition";

/// <summary>
/// Field logical names.
/// </summary>
public static class Fields
{
/// <summary>
/// The schema name.
/// </summary>
public const string SchemaName = "schemaname";
}
}

/// <summary>
/// Constants relating to the environmentvariablevalue entity.
/// </summary>
public static class EnvironmentVariableValue
{
/// <summary>
/// The logical name.
/// </summary>
public const string LogicalName = "environmentvariablevalue";

/// <summary>
/// Field logical names.
/// </summary>
public static class Fields
{
/// <summary>
/// The variable definition id.
/// </summary>
public const string EnvironmentVariableDefinitonId = "environmentvariabledefinitionid";

/// <summary>
/// The variable value.
/// </summary>
public const string Value = "value";
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
/// </summary>
public abstract class PackageTemplateBase : ImportExtension
{
#region private-props

private ICrmServiceAdapter crmServiceAdapter;
private string licensedUsername;
private TemplateConfig templateConfig;
Expand All @@ -32,6 +34,11 @@ public abstract class PackageTemplateBase : ImportExtension
private TableColumnProcessingService autonumberSeedSettingSvc;
private MailboxDeploymentService mailboxSvc;

private EnvironmentVariableDeploymentService environmentVariableService;

#endregion
#region protected-props

/// <summary>
/// Gets a value indicating whether whether the deployment is running on Azure DevOps.
/// </summary>
Expand Down Expand Up @@ -76,37 +83,62 @@ protected string LicensedUsername
protected IDictionary<string, string> MailboxMappings => this.GetSettings(Constants.Settings.MailboxPrefix);

/// <summary>
/// Gets an extended <see cref="Microsoft.Xrm.Sdk.IOrganizationService"/>.
/// Gets the PowerApps environment variables mappings.
/// </summary>
/// <value>
/// An extended <see cref="Microsoft.Xrm.Sdk.IOrganizationService"/>.
/// </value>
protected ICrmServiceAdapter CrmServiceAdapter
/// <returns>The Power App environment variables.</returns>
protected IDictionary<string, string> PowerAppsEnvironmentVariables => this.GetSettings(Constants.Settings.PowerAppsEnvironmentVariablePrefix);

/// <summary>
/// Gets a list of solutions that have been processed (i.e. <see cref="PreSolutionImport(string, bool, bool, out bool, out bool)"/> has been ran for that solution.)
/// </summary>
protected IList<string> ProcessedSolutions
{
get
{
if (this.crmServiceAdapter == null)
if (this.processedSolutions == null)
{
this.crmServiceAdapter = new CrmServiceAdapter(this.CrmSvc, this.TraceLoggerAdapter);
this.processedSolutions = new List<string>();
}

return this.crmServiceAdapter;
return this.processedSolutions;
}
}

/// <summary>
/// Gets a list of solutions that have been processed (i.e. <see cref="PreSolutionImport(string, bool, bool, out bool, out bool)"/> has been ran for that solution.)
/// Gets provides access to the templateconfig section of the ImportConfig.xml.
/// </summary>
protected IList<string> ProcessedSolutions
protected TemplateConfig TemplateConfig
{
get
{
if (this.processedSolutions == null)
if (this.templateConfig == null)
{
this.processedSolutions = new List<string>();
this.templateConfig = TemplateConfig.Load(this.ImportConfigFilePath);
}

return this.processedSolutions;
return this.templateConfig;
}
}

#endregion
#region service-initialisers

/// <summary>
/// Gets an extended <see cref="Microsoft.Xrm.Sdk.IOrganizationService"/>.
/// </summary>
/// <value>
/// An extended <see cref="Microsoft.Xrm.Sdk.IOrganizationService"/>.
/// </value>
protected ICrmServiceAdapter CrmServiceAdapter
{
get
{
if (this.crmServiceAdapter == null)
{
this.crmServiceAdapter = new CrmServiceAdapter(this.CrmSvc, this.TraceLoggerAdapter);
}

return this.crmServiceAdapter;
}
}

Expand Down Expand Up @@ -239,18 +271,18 @@ protected MailboxDeploymentService MailboxSvc
}

/// <summary>
/// Gets provides access to the templateconfig section of the ImportConfig.xml.
/// Gets provides deployment functionality relating to environment variables.
/// </summary>
protected TemplateConfig TemplateConfig
protected EnvironmentVariableDeploymentService EnvironmentVariablesSvc
{
get
{
if (this.templateConfig == null)
if (this.environmentVariableService == null)
{
this.templateConfig = TemplateConfig.Load(this.ImportConfigFilePath);
this.environmentVariableService = new EnvironmentVariableDeploymentService(this.TraceLoggerAdapter, this.CrmServiceAdapter);
}

return this.templateConfig;
return this.environmentVariableService;
}
}

Expand All @@ -270,6 +302,9 @@ protected TraceLoggerAdapter TraceLoggerAdapter
}
}

#endregion
#region lifecycle-events

/// <inheritdoc/>
public override void InitializeCustomExtension()
{
Expand Down Expand Up @@ -330,6 +365,8 @@ public override bool AfterPrimaryImport()
this.TemplateConfig.PostDeployDataImports,
this.PackageFolderPath);
this.EnvironmentVariablesSvc.SetEnvironmentVariables(this.PowerAppsEnvironmentVariables);
this.SdkStepSvc.SetStatesBySolution(
this.ProcessedSolutions,
this.TemplateConfig.SdkStepsToDeactivate.Select(s => s.Name));
Expand Down Expand Up @@ -376,6 +413,9 @@ public override bool AfterPrimaryImport()
return true;
}

#endregion
#region settings-retrival

/// <summary>
/// Gets a setting either from runtime arguments or an environment variable (in that order of preference). Environment variables should be prefixed with 'PACKAGEDEPLOYER_SETTINGS_'.
/// </summary>
Expand Down Expand Up @@ -462,6 +502,9 @@ protected IDictionary<string, string> GetSettings(string prefix)
return mappings;
}

#endregion
#region lifecycle-event-helpers

private void ExecuteLifecycleEvent(string eventName, Action eventAction)
{
this.LogLifecycleEventStart(eventName);
Expand All @@ -479,6 +522,9 @@ private void LogLifecycleEventEnd(string lifecycleEvent)
this.TraceLoggerAdapter.LogInformation($"{nameof(PackageTemplateBase)}.{lifecycleEvent} completed.");
}

#endregion
#region logging-helpers

// Excluded as it would require our CI or PR validation pipelines to be partially succeeding or failing
[ExcludeFromCodeCoverage]
private void LogTaskCompleteResult()
Expand All @@ -492,5 +538,7 @@ private void LogTaskCompleteResult()
Console.WriteLine("##vso[task.complete result=SucceededWithIssues;]DONE");
}
}

#endregion
}
}
Loading

0 comments on commit b052665

Please sign in to comment.