internal/functions/publish-d365foresources.ps1
<# .SYNOPSIS Publish resources .DESCRIPTION Publishes Dynamics 365 for Finance and Operations resources to the publishing directory. .PARAMETER ResourceTypes The types of resources to publish. .PARAMETER PublishingDirectory The directory to publish the resources to. Each resource type will be published to a subdirectory. .PARAMETER PackageDirectory The directory containing the resources. .PARAMETER AosServiceWebRootPath The path to the AOS service web root containing the metadata assemblies to access the resources. .EXAMPLE PS C:\> Publish-D365FOResources -ResourceTypes Images,Scripts,Styles,Html -PublishingDirectory C:\temp\resources This will publish the resources of the types Images, Scripts, Styles, and Html to the directory C:\temp\resources. .NOTES Author: Florian Hopfner (@FH-Inway) #> function Publish-D365FOResources { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Publish-D365FOResources')] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]] $ResourceTypes, [Parameter(Mandatory = $true)] [string] $PublishingDirectory, [Parameter(Mandatory = $false)] [PsfDirectory] $PackageDirectory = $Script:PackageDirectory, [Parameter(Mandatory = $false)] [PsfDirectory] $AosServiceWebRootPath = $Script:AOSPath ) Invoke-TimeSignal -Start Write-PSFMessage -Level Verbose -Message "Initializing resources publishing." $resourcesDirectory = $PublishingDirectory foreach ($resourceType in $ResourceTypes) { $resourceTypeDirectory = Join-Path $resourcesDirectory $resourceType Test-PathExists -Path $resourceTypeDirectory -Type Container -Create | Out-Null } Import-Assemblies -AosServiceWebRootPath $AosServiceWebRootPath # For unknown reasons, the provider cannot be initialized in a separate function. # If this is done, $metadataProviderViaRuntime.Resources is null. Write-PSFMessage -Level Debug -Message "Initializing metadata runtime provider." $runtimeProviderConfiguration = New-Object Microsoft.Dynamics.AX.Metadata.Storage.Runtime.RuntimeProviderConfiguration -ArgumentList $PackageDirectory $metadataProviderFactoryViaRuntime = New-Object Microsoft.Dynamics.AX.Metadata.Storage.MetadataProviderFactory $metadataProviderViaRuntime = $metadataProviderFactoryViaRuntime.CreateRuntimeProvider($runtimeProviderConfiguration) Write-PSFMessage -Level Verbose -Message "Starting resources publishing" $resources = $metadataProviderViaRuntime.Resources.GetPrimaryKeys() foreach ($resourceItem in $resources) { $params = @{ ResourceItem = $resourceItem MetadataProviderViaRuntime = $metadataProviderViaRuntime ResourceTypes = $ResourceTypes ResourcesDirectory = $resourcesDirectory } Publish-Resource @params } Write-PSFMessage -Level Host -Message "Resources publishing completed." Invoke-TimeSignal -End } function Import-Assemblies { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Import-Assemblies')] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [PsfDirectory] $AosServiceWebRootPath ) Write-PSFMessage -Level Debug -Message "Importing required assemblies." [System.Collections.ArrayList] $Files2Process = New-Object -TypeName "System.Collections.ArrayList" $binDir = Join-Path $AosServiceWebRootPath "bin" $null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.AX.Metadata.Core.dll)) $null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.AX.Metadata.dll)) $null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.AX.Metadata.Storage.dll)) $null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.Performance.Instrumentation.dll)) $null = $Files2Process.Add((Join-Path $binDir Microsoft.Dynamics.ApplicationPlatform.XppServices.Instrumentation.dll)) Import-AssemblyFileIntoMemory -Path $($Files2Process.ToArray()) } function Publish-Resource { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [object] $ResourceItem, [Parameter(Mandatory = $true)] [object] $MetadataProviderViaRuntime, [Parameter(Mandatory = $true)] [string[]] $ResourceTypes, [Parameter(Mandatory = $true)] [string] $ResourcesDirectory ) $resourceName = [System.String]$ResourceItem Write-PSFMessage -Level Debug -Message "Processing resource '$resourceName'." $resourceHeader = New-Object -TypeName Microsoft.Dynamics.AX.Metadata.MetaModel.MetaReadHeader $resource = $MetadataProviderViaRuntime.Resources.Read($resourceName, [ref]$resourceHeader) $resourceTimestamp = $MetadataProviderViaRuntime.Resources.GetContentTimestampUtc($resource, $resourceHeader) $resourceType = $resource.TypeOfResource $resourcePath = Join-Path $ResourcesDirectory $resourceType $resourceFilePath = Join-Path $resourcePath $resource.FileName Write-PSFMessage -Level Debug -Message "Checking resource '$resourceName' of type '$resourceType' for publishing." $resourceData = @{ ResourceType = $resourceType ResourceName = $resourceName ResourceTimestamp = $resourceTimestamp } $params = @{ ResourceData = $resourceData AllowedResourceTypes = $ResourceTypes ResourceFilePath = $resourceFilePath } $shouldPublishResource = Test-PublishResource @params if (-not $shouldPublishResource) { Write-PSFMessage -Level Debug -Message "Resource '$resourceName' is not being published." continue } Write-PSFMessage -Level Debug -Message "Publishing resource '$resourceName' to '$resourceFilePath'." $sourceStream = $MetadataProviderViaRuntime.Resources.GetContent($resource, $resourceHeader) if ($sourceStream) { $argumentList = @( $resourceFilePath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write ) $targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $argumentList $sourceStream.CopyTo($targetStream) $sourceStream.Close() $targetStream.Close() Write-PSFMessage -Level Debug -Message "Resource '$resourceName' published successfully." } } function Test-PublishResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $true)] [hashtable] $ResourceData, [Parameter(Mandatory = $true)] [string[]] $AllowedResourceTypes, [Parameter(Mandatory = $true)] [string] $ResourceFilePath ) $resourceType = $ResourceData.ResourceType $resourceName = $ResourceData.ResourceName $resourceTimestamp = $ResourceData.ResourceTimestamp $isAResourceTypeToPublish = $AllowedResourceTypes -contains $resourceType if (-not $isAResourceTypeToPublish) { Write-PSFMessage -Level Debug -Message "Resource '$resourceName' of type '$resourceType' is not a resource type to publish. Skipping." return $false } if (-not (Test-PathExists -Path $ResourceFilePath -Type Leaf -WarningAction SilentlyContinue -ErrorAction SilentlyContinue)) { Write-PSFMessage -Level Debug -Message "Resource '$resourceName' does not exist. Will be published." return $true } $existingFileTimestamp = (Get-ItemProperty $resourceFilePath).LastWriteTimeUtc if ($existingFileTimestamp -ne $ResourceTimestamp) { Write-PSFMessage -Level Debug -Message "Resource '$ResourceName' is outdated (resource time stamp: $ResouceTimestamp, existing file time stamp: $existingFileTimestamp). Will be published." return $true } Write-PSFMessage -Level Debug -Message "Resource '$ResourceName' is up to date." return $false } |