commands.ps1
<# .SYNOPSIS Clear the imported AX 2012 PowerShell modules .DESCRIPTION Removes the different AX 2012 PowerShell modules that has been loaded into the session. .EXAMPLE PS C:\> Clear-Ax2012StandardPowershellModule This will remove all the known AX 2012 PowerShell modules that have been loaded. It is connected to the use of Import-Module $Script:AxPowerShellModule .NOTES Author: M�tz Jensen (@Splaxi) #> function Clear-Ax2012StandardPowershellModule { [CmdletBinding()] param ( ) Remove-Module @("AxUtilLib", "AxUtilLib.PowerShell", "Microsoft.Dynamics.Administration", "Microsoft.Dynamics.AX.Framework.Management", "Microsoft.Dynamics.ManagementUtilities") } <# .SYNOPSIS Convert HashTable into an array .DESCRIPTION Convert HashTable into an array of Key and Value .PARAMETER InputObject The HashTable object that you want to work against .PARAMETER KeyPrefix The prefix that you want to append to the key of the HashTable The default value is "-" .PARAMETER ValuePrefix The prefix that you want to append to the value of the HashTable The default value is " " .EXAMPLE PS C:\> $params = @{DatabaseServer = "Localhost"; DatabaseName = "MicrosoftDynamicsAx_model"} PS C:\> $arguments = Convert-HashToArgString -Inputs $params This will convert the $params into an array of strings, each with the Key and Value. .NOTES Author: M�tz Jensen (@Splaxi) #> function Convert-HashToArgString { [CmdletBinding()] param ( [HashTable] $InputObject, [string] $KeyPrefix = "-", [string] $ValuePrefix = " " ) $InputObject.Keys | ForEach-Object { "$KeyPrefix$($_)$ValuePrefix`"$($InputObject.Item($_))`""} } <# .SYNOPSIS Convert HashTable into an array .DESCRIPTION Convert HashTable with switches inside into an array of Key:Value .PARAMETER InputObject The HashTable object that you want to work against Shold only contain Key / Vaule, where value is $true or $false .PARAMETER KeyPrefix The prefix that you want to append to the key of the HashTable The default value is "-" .PARAMETER ValuePrefix The prefix that you want to append to the value of the HashTable The default value is ":" .EXAMPLE PS C:\> $params = @{NoPrompt = $true; CreateParents = $false} PS C:\> $arguments = Convert-HashToArgStringSwitch -Inputs $params This will convert the $params into an array of strings, each with the Key:Value. .NOTES Author: M�tz Jensen (@Splaxi) #> function Convert-HashToArgStringSwitch { [CmdletBinding()] [OutputType([System.String])] param ( [HashTable] $InputObject, [string] $KeyPrefix = "-", [string] $ValuePrefix = ":" ) foreach ($key in $InputObject.Keys) { $value = "`${0}" -f $InputObject.Item($key).ToString().ToLower() "$KeyPrefix$($key)$ValuePrefix$($value)" } } <# .SYNOPSIS Get details about an AX 2012 AOS instance .DESCRIPTION Get all the technical details about an AX 2012 AOS instance .PARAMETER RegistryPath Path to the registry for the specific AX 2012 AOS instance .EXAMPLE PS C:\> Get-AxAosInstanceDetails -RegistryPath "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Dynamics Server\6.0\01" This will traverse all the details about the first installed AX 2012 AOS instance in the registry. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxAosInstanceDetails { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] param( [string] $RegistryPath ) Write-PSFMessage -Level Verbose -Message "Working against $RegistryPath" -Target $RegistryPath $RegKey = Get-Item -Path $RegistryPath.Replace("HKEY_LOCAL_MACHINE", "HKLM:") $RegOuter = Get-ItemProperty -Path ($RegKey.Name).Replace("HKEY_LOCAL_MACHINE", "HKLM:") $RegInner = Get-ItemProperty -Path (Join-Path $RegKey.Name $RegOuter.Current).Replace("HKEY_LOCAL_MACHINE", "HKLM:") $BuildNumbers = Get-FileVersion -Path $(Join-Path $RegInner.bindir "Ax32Serv.exe") $InstanceDetail = [Ordered]@{} $InstanceDetail.InstanceName = $RegOuter.InstanceName $InstanceDetail.ConfigurationName = $RegOuter.Current $InstanceDetail.BinDirectory = $RegInner.bindir $InstanceDetail.ExecutablePath = Join-Path $RegInner.bindir "Ax32Serv.exe" $InstanceDetail.FileVersion = $BuildNumbers.FileVersion $InstanceDetail.ProductVersion = $BuildNumbers.ProductVersion $InstanceDetail.FileVersionUpdated = $BuildNumbers.FileVersionUpdated $InstanceDetail.ProductVersionUpdated = $BuildNumbers.ProductVersionUpdated $InstanceDetail.DatabaseServer = $RegInner.dbserver $InstanceDetail.DatabaseName = $RegInner.database $InstanceDetail.ModelstoreDatabase = "$($RegInner.database)_model" $InstanceDetail.AosPort = $RegInner.port $InstanceDetail.WsdlPort = $RegInner.WSDLPort $InstanceDetail.NetTcpPort = $RegInner.NetTCPPort $InstanceDetail.RegistryKeyPath = $RegKey.Name $InstanceDetail.InstanceNumber = Split-Path -Path $RegKey.Name -Leaf $InstanceDetail.ComputerName = "$env:computername" [PSCustomObject] $InstanceDetail } <# .SYNOPSIS Get the AX 2012 Client bin directory .DESCRIPTION Get the AX 2012 Client bin directory from the registry .EXAMPLE PS C:\> Get-ClientBinDir This will get the full path for the AX 2012 client bin directory .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-ClientBinDir { [CmdletBinding()] [OutputType('System.String')] param ( ) $RegKey = Get-Item -Path $Script:RegistryClient -ErrorAction SilentlyContinue if (-not ($null -eq $RegKey)) { Join-Path $RegKey.GetValue("InstallDir32") "Client\Bin" } } <# .SYNOPSIS Clone a hashtable .DESCRIPTION Create a deep clone of a hashtable for you to work on it without updating the original object .PARAMETER InputObject The hashtable you want to clone .EXAMPLE PS C:\> Get-DeepClone -InputObject $HashTable This will clone the $HashTable variable into a new object and return it to you. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-DeepClone { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')] [CmdletBinding()] param( [parameter(Mandatory = $true)] $InputObject ) process { if ($InputObject -is [hashtable]) { $clone = @{} foreach ($key in $InputObject.keys) { $clone[$key] = Get-DeepClone $InputObject[$key] } $clone } else { $InputObject } } } <# .SYNOPSIS Get the file version details .DESCRIPTION Get the file version details for any given file .PARAMETER Path Path to the file that you want to extract the file version details from .EXAMPLE PS C:\> Get-FileVersion -Path "C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\Bin\AxServ32.exe" This will get the file version details for the AX AOS executable (AxServ32.exe). .NOTES Author: M�tz Jensen (@Splaxi) Inspired by https://blogs.technet.microsoft.com/askpfeplat/2014/12/07/how-to-correctly-check-file-versions-with-powershell/ #> function Get-FileVersion { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string] $Path ) if (-not (Test-PathExists -Path $Path -Type Leaf)) { return } Write-PSFMessage -Level Verbose -Message "Extracting the file properties for: $Path" -Target $Path $Filepath = Get-Item -Path $Path [PSCustomObject]@{ FileVersion = $Filepath.VersionInfo.FileVersion ProductVersion = $Filepath.VersionInfo.ProductVersion FileVersionUpdated = "$($Filepath.VersionInfo.FileMajorPart).$($Filepath.VersionInfo.FileMinorPart).$($Filepath.VersionInfo.FileBuildPart).$($Filepath.VersionInfo.FilePrivatePart)" ProductVersionUpdated = "$($Filepath.VersionInfo.ProductMajorPart).$($Filepath.VersionInfo.ProductMinorPart).$($Filepath.VersionInfo.ProductBuildPart).$($Filepath.VersionInfo.ProductPrivatePart)" } } <# .SYNOPSIS Get key from HashTable .DESCRIPTION Get specific key(s) from a HashTable returned as a HashTable .PARAMETER InputObject The HashTable that you want to extract key(s) from .PARAMETER Keys Names of the key(s) that you want to to extract from the HashTable .EXAMPLE PS C:\> $params = @{NoPrompt = $true; CreateParent = $false} Get-HashtableKey -InputObject $params -Keys "NoPrompt" This will return a new HashTable only containing the "NoPrompt" entry. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-HashtableKey { [CmdletBinding()] [OutputType([HashTable])] param ( [parameter(Mandatory = $true)] [HashTable] $InputObject, [Parameter(Mandatory = $true)] [string[]] $Keys ) $var = Get-DeepClone $InputObject $res = @{} foreach ($key in $var.Keys) { Write-PSFMessage -Level Verbose -Message "Working on key: $key" -Target $key if($Keys.Contains($key)) { Write-PSFMessage -Level Verbose -Message "Found key: $key" -Target $key $null = $res.Add($key, $var.Item($key)) } } $res } <# .SYNOPSIS Get service list from HashTable .DESCRIPTION Extract the services from the list of entries in the HashTable .PARAMETER AllAxServices Instruct the cmdlet to return all services .PARAMETER Aos Instruct the cmdlet to return AOS .PARAMETER ManagementReporter Instruct the cmdlet to return ManagementReporter .PARAMETER DIXF Instruct the cmdlet to return DIXF .EXAMPLE PS C:\> Get-ServiceList -All This will return all services that the cmdlet knows about. .NOTES Author: M�tz Jensen (@Splaxi) #> Function Get-ServiceList { [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $false, ParameterSetName = 'Default', Position = 1 )] [switch] $AllAxServices, [Parameter(Mandatory = $false, ParameterSetName = 'Specific', Position = 2 )] [switch] $Aos, [Parameter(Mandatory = $false, ParameterSetName = 'Specific', Position = 3 )] [switch] $ManagementReporter, [Parameter(Mandatory = $false, ParameterSetName = 'Specific', Position = 4 )] [switch] $DIXF ) if ($PSCmdlet.ParameterSetName -eq "Specific") { $AllAxServices = $false } Write-PSFMessage -Level Verbose -Message "The PSBoundParameters was" -Target ($PSBoundParameters.Keys -Join "," ) $mrProcessName = "MR2012ProcessService" $mrApplicationName = "MR2012ApplicationService" $dixfName = "Microsoft.Dynamics.AX.Framework.Tools.DMF.SSISHelperService.exe" [System.Collections.ArrayList]$Services = New-Object -TypeName "System.Collections.ArrayList" if ($AllAxServices) { for ($i = 1; $i -lt 100; $i++) { $null = $Services.Add("AOS60`$$($i.ToString("00"))") } $null = $Services.AddRange(@($mrProcessName, $mrApplicationName, $dixfName)) } else { if ($Aos) { for ($i = 1; $i -lt 100; $i++) { $null = $Services.Add("AOS60`$$($i.ToString("00"))") } } if ($ManagementReporter) { $null = $Services.Add($mrProcessName) $null = $Services.Add($mrApplicationName) } if ($DIXF) { $null = $Services.Add($dixfName) } } $Services.ToArray() } <# .SYNOPSIS Get a SqlCommand object .DESCRIPTION Get a SqlCommand object initialized with the passed parameters .PARAMETER DatabaseServer The name of the database server .PARAMETER DatabaseName The name of the database .PARAMETER SqlUser The login name for the SQL Server instance .PARAMETER SqlPwd The password for the SQL Server user. .PARAMETER TrustedConnection Should the connection use a Trusted Connection or not .EXAMPLE PS C:\> Get-SqlCommand -DatabaseServer localhost -DatabaseName MicrosoftDynamicsAx_model -SqlUser User123 -SqlPwd "Password123" -TrustedConnection $false This will initialize a new SqlCommand object (.NET type) with localhost as the server name, AxDB as the database and the User123 sql credentials. .EXAMPLE PS C:\> Get-SqlCommand -DatabaseServer localhost -DatabaseName MicrosoftDynamicsAx_model -TrustedConnection $true This will initialize a new SqlCommand object (.NET type) with localhost as the server name, AxDB as the database and use the current windows credentials (trusted connection). .NOTES Author: Rasmus Andersen (@ITRasmus) Author: M�tz Jensen (@Splaxi) #> function Get-SQLCommand { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $DatabaseServer, [Parameter(Mandatory = $true)] [string] $DatabaseName, [Parameter(Mandatory = $false)] [string] $SqlUser, [Parameter(Mandatory = $false)] [string] $SqlPwd, [Parameter(Mandatory = $false)] [boolean] $TrustedConnection ) Write-PSFMessage -Level Debug -Message "Writing the bound parameters" -Target $PsBoundParameters [System.Collections.ArrayList]$Params = New-Object -TypeName "System.Collections.ArrayList" $null = $Params.Add("Server='$DatabaseServer';") $null = $Params.Add("Database='$DatabaseName';") if ($null -eq $TrustedConnection -or (-not $TrustedConnection)) { $null = $Params.Add("User='$SqlUser';") $null = $Params.Add("Password='$SqlPwd';") } else { $null = $Params.Add("Integrated Security='SSPI';") } $null = $Params.Add("Application Name='ax2012.tools'") Write-PSFMessage -Level Verbose -Message "Building the SQL connection string." -Target ($Params.ToArray() -join ",") $sqlConnection = New-Object System.Data.SqlClient.SqlConnection try { $sqlConnection.ConnectionString = ($Params -join "") $sqlCommand = New-Object System.Data.SqlClient.SqlCommand $sqlCommand.Connection = $sqlConnection $sqlCommand.CommandTimeout = 0 } catch { Write-PSFMessage -Level Host -Message "Something went wrong while working with the sql server connection objects" -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of errors" return } $sqlCommand } <# .SYNOPSIS Get the size from the parameter .DESCRIPTION Get the size from the parameter based on its datatype and value .PARAMETER SqlParameter The SqlParameter object that you want to get the size from .EXAMPLE PS C:\> $SqlCmd = New-Object System.Data.SqlClient.SqlCommand PS C:\> $SqlCmd.Parameters.AddWithValue("@Parm1", "1234") PS C:\> Get-SqlParameterSize -SqlParameter $SqlCmd.Parameters[0] This will extract the size from the first parameter from the SqlCommand object and return it as a formatted string. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-SqlParameterSize { [CmdletBinding()] [OutputType('System.String')] param ( [System.Data.SqlClient.SqlParameter] $SqlParameter ) $res = "" $stringSizeTypes = @( [System.Data.SqlDbType]::Char, [System.Data.SqlDbType]::NChar, [System.Data.SqlDbType]::NText, [System.Data.SqlDbType]::NVarChar, [System.Data.SqlDbType]::Text, [System.Data.SqlDbType]::VarChar ) if ( $stringSizeTypes -contains $SqlParameter.SqlDbType) { $res = "($($SqlParameter.Size))" } $res } <# .SYNOPSIS Get the value from the parameter .DESCRIPTION Get the value that is assigned to the SqlParameter object .PARAMETER SqlParameter The SqlParameter object that you want to work against .EXAMPLE PS C:\> $SqlCmd = New-Object System.Data.SqlClient.SqlCommand PS C:\> $SqlCmd.Parameters.AddWithValue("@Parm1", "1234") PS C:\> Get-SqlParameterValue -SqlParameter $SqlCmd.Parameters[0] This will extract the value from the first parameter from the SqlCommand object. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-SqlParameterValue { [CmdletBinding()] [OutputType('System.String')] param ( [System.Data.SqlClient.SqlParameter] $SqlParameter ) $result = $null $stringEscaped = @( [System.Data.SqlDbType]::Char, [System.Data.SqlDbType]::DateTime, [System.Data.SqlDbType]::NChar, [System.Data.SqlDbType]::NText, [System.Data.SqlDbType]::NVarChar, [System.Data.SqlDbType]::Text, [System.Data.SqlDbType]::VarChar, [System.Data.SqlDbType]::Xml, [System.Data.SqlDbType]::Date, [System.Data.SqlDbType]::Time, [System.Data.SqlDbType]::DateTime2, [System.Data.SqlDbType]::DateTimeOffset ) $stringNumbers = @([System.Data.SqlDbType]::Float, [System.Data.SqlDbType]::Decimal) switch ($SqlParameter.SqlDbType) { { $stringEscaped -contains $_ } { $result = "'{0}'" -f $SqlParameter.Value.ToString().Replace("'", "''") break } { [System.Data.SqlDbType]::Bit } { if ((ConvertTo-BooleanOrDefault -Object $SqlParameter.Value.ToString() -Default $true)) { $result = '1' } else { $result = '0' } break } { $stringNumbers -contains $_ } { $SqlParameter.Value $result = ([System.Double]$SqlParameter.Value).ToString([System.Globalization.CultureInfo]::InvariantCulture).Replace("'", "''") break } default { $result = $SqlParameter.Value.ToString().Replace("'", "''") break } } $result } <# .SYNOPSIS Get an executable string from a SqlCommand object .DESCRIPTION Get an formatted and valid string from a SqlCommand object that contains all variables .PARAMETER SqlCommand The SqlCommand object that you want to retrieve the string from .EXAMPLE PS C:\> $SqlCmd = New-Object System.Data.SqlClient.SqlCommand PS C:\> $SqlCmd.CommandText = "SELECT * FROM Table WHERE Column = @Parm1" PS C:\> $SqlCmd.Parameters.AddWithValue("@Parm1", "1234") PS C:\> Get-SqlString -SqlCommand $SqlCmd .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-SqlString { [CmdletBinding()] [OutputType('System.String')] param ( [System.Data.SqlClient.SqlCommand] $SqlCommand ) $sbDeclare = [System.Text.StringBuilder]::new() $sbAssignment = [System.Text.StringBuilder]::new() $sbRes = [System.Text.StringBuilder]::new() if ($SqlCommand.CommandType -eq [System.Data.CommandType]::Text) { if (-not ($null -eq $SqlCommand.Connection)) { $null = $sbDeclare.Append("USE ").AppendLine($SqlCommand.Connection.Database) } foreach ($parameter in $SqlCommand.Parameters) { if ($parameter.Direction -eq [System.Data.ParameterDirection]::Input) { $null = $sbDeclare.Append("DECLARE ").Append($parameter.ParameterName).Append("`t") $null = $sbDeclare.Append($parameter.SqlDbType.ToString().ToUpper()) $null = $sbDeclare.AppendLine((Get-SqlParameterSize -SqlParameter $parameter)) $null = $sbAssignment.Append("SET ").Append($parameter.ParameterName).Append(" = ").AppendLine((Get-SqlParameterValue -SqlParameter $parameter)) } } $null = $sbRes.AppendLine($sbDeclare.ToString()) $null = $sbRes.AppendLine($sbAssignment.ToString()) $null = $sbRes.AppendLine($SqlCommand.CommandText) } $sbRes.ToString() } <# .SYNOPSIS Get WMDP details from the IIS .DESCRIPTION Get all the necessary details from the IIS about the WMDP installation .EXAMPLE PS C:\> Get-WMDPDetailsFromIIS This will get details from all the WMDP installations on the server. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-WMDPDetailsFromIIS { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] param ( ) if ((Get-Module -ListAvailable -Name "IISAdministration").Count -lt 1) { Write-PSFMessage -Level Host -Message "It seems that you didn't have the <c='em'>extended</c> powershell administration tools for IIS installed. For Windows Server 2012 and 2012 R2 it is required that you install this module directly." Write-PSFMessage -Level Host -Message "Install-Module IISAdministration -Force -Confirm:`$false" Stop-PSFFunction -Message "Stopping because of missing parameters" -StepsUpward 1 return } else { $null = Import-Module -name "IISAdministration" -Force } $sites = Get-IISSite | Where-Object {$_.Applications.VirtualDirectories.PhysicalPath -like "*AX*warehouse*portal*"} foreach ($site in $sites) { $res = [Ordered]@{SiteId = $site.Id; SiteName = $site.Name; SiteStatus = $site.State; SiteBindings = $site.Bindings; Path = $site.Applications.VirtualDirectories.PhysicalPath MvcViewPath = Join-Path $site.Applications.VirtualDirectories.PhysicalPath "Views\Execute" CssPath = Join-Path $site.Applications.VirtualDirectories.PhysicalPath "Content\CSS\RFCSS" } $appPool = Get-IISAppPool | Where-Object Name -eq $site.Applications.ApplicationPoolName $res.AppPoolName = $appPool.Name $res.AppPoolStatus = $appPool.Status $res.AppPoolIdentity = $appPool.ProcessModel.Username [PSCustomObject]$res } } <# .SYNOPSIS Invoke a process .DESCRIPTION Invoke a process and pass the needed parameters to it .PARAMETER Path Path to the program / executable that you want to start .PARAMETER Params Array of string parameters that you want to pass to the executable .PARAMETER ShowOriginalProgress Instruct the cmdlet to show the standard output in the console Default is $false which will silence the standard output .PARAMETER OutputCommandOnly Instruct the cmdlet to only output the command that you would have to execute by hand Will include full path to the executable and the needed parameters based on your selection .PARAMETER EnableException This parameters disables user-friendly warnings and enables the throwing of exceptions This is less user friendly, but allows catching exceptions in calling scripts .EXAMPLE PS C:\> Invoke-Process -Path "C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\AXBuild.exe" -Params "xppcompileall","/altbin=`"C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin`"","/aos=01","/dbserver=`"SQLTEST`"","/modelstore=`"AXTEST_model`"","/log=`"c:\temp\ax2012.tools\AxBuildLog`"","/compiler=`"C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\ax32serv.exe`"" This will invoke the "C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\AXBuild.exe" executable. All parameters will be passed to it. The standard output will be redirected to a local variable. The error output will be redirected to a local variable. The standard output will be written to the verbose stream before exiting. If an error should occur, both the standard output and error output will be written to the console / host. .EXAMPLE PS C:\> Invoke-Process -OutputCommandOnly -Path "C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\AXBuild.exe" -Params "xppcompileall","/altbin=`"C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin`"","/aos=01","/dbserver=`"SQLTEST`"","/modelstore=`"AXTEST_model`"","/log=`"c:\temp\ax2012.tools\AxBuildLog`"","/compiler=`"C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\ax32serv.exe`"" This will generate the command for the "C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\AXBuild.exe" executable. All parameters will be included in the output command. .EXAMPLE PS C:\> Invoke-Process -ShowOriginalProgress -Path "C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\AXBuild.exe" -Params "xppcompileall","/altbin=`"C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin`"","/aos=01","/dbserver=`"SQLTEST`"","/modelstore=`"AXTEST_model`"","/log=`"c:\temp\ax2012.tools\AxBuildLog`"","/compiler=`"C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\ax32serv.exe`"" This will invoke the "C:\Program Files\Microsoft Dynamics AX\60\Server\AXTEST\Bin\AXBuild.exe" executable. All parameters will be passed to it. The standard output will be outputted directly to the console / host. The error output will be outputted directly to the console / host. .NOTES Author: M�tz Jensen (@Splaxi) #> function Invoke-Process { [CmdletBinding()] [OutputType([System.String], ParameterSetName="Generate")] param ( [Parameter(Mandatory = $true)] [Alias('Executable')] [string] $Path, [Parameter(Mandatory = $true)] [string[]] $Params, [switch] $ShowOriginalProgress, [Parameter(ParameterSetName = "Generate")] [switch] $OutputCommandOnly, [switch] $EnableException ) Invoke-TimeSignal -Start if (-not (Test-PathExists -Path $Path -Type Leaf)) {return} if (Test-PSFFunctionInterrupt) { return } $tool = Split-Path -Path $Path -Leaf $pinfo = New-Object System.Diagnostics.ProcessStartInfo $pinfo.FileName = "$Path" $pinfo.WorkingDirectory = Split-Path -Path $Path -Parent if (-not $ShowOriginalProgress) { Write-PSFMessage -Level Verbose "Output and Error streams will be redirected (silence mode)" $pinfo.RedirectStandardError = $true $pinfo.RedirectStandardOutput = $true } $pinfo.UseShellExecute = $false $pinfo.Arguments = "$($Params -join " ")" $p = New-Object System.Diagnostics.Process $p.StartInfo = $pinfo Write-PSFMessage -Level Verbose "Starting the $tool" -Target "$($params -join " ")" if($OutputCommandOnly){ Write-PSFMessage -Level Host "$Path $($pinfo.Arguments)" return } $p.Start() | Out-Null if (-not $ShowOriginalProgress) { $stdout = $p.StandardOutput.ReadToEnd() $stderr = $p.StandardError.ReadToEnd() } Write-PSFMessage -Level Verbose "Waiting for the $tool to complete" $p.WaitForExit() if ($p.ExitCode -ne 0 -and (-not $ShowOriginalProgress)) { Write-PSFMessage -Level Host "Exit code from $tool indicated an error happened. Will output both standard stream and error stream." Write-PSFMessage -Level Host "Standard output was: \r\n $stdout" Write-PSFMessage -Level Host "Error output was: \r\n $stderr" $messageString = "Stopping because an Exit Code from $tool wasn't 0 (zero) like expected." Stop-PSFFunction -Message "Stopping because of Exit Code." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>',''))) -StepsUpward 1 return } else { Write-PSFMessage -Level Verbose "Standard output was: \r\n $stdout" } Invoke-TimeSignal -End } <# .SYNOPSIS Invoke timing logic .DESCRIPTION Invoke timing logic that keeps track of the time spend inside a function .PARAMETER Start Instruct the cmdlet that the starting of measurement .PARAMETER End Instruct the cmdlet that the ending of measurement .EXAMPLE PS C:\> Invoke-TimeSignal -Start This will start the timing measurement. .EXAMPLE PS C:\> Invoke-TimeSignal -End This will end the timing measurement and have the cmdlet write the details into the verbose log. .NOTES Author: M�tz Jensen (@Splaxi) #> function Invoke-TimeSignal { [CmdletBinding(DefaultParameterSetName = 'Start')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Start', Position = 1 )] [switch] $Start, [Parameter(Mandatory = $True, ParameterSetName = 'End', Position = 2 )] [switch] $End ) $Time = (Get-Date) $Command = (Get-PSCallStack)[1].Command if($Start.IsPresent) { if($Script:TimeSignals.ContainsKey($Command)) { Write-PSFMessage -Level Verbose -Message "The command '$Command' was already taking part in time measurement. The entry has been update with current date and time." $Script:TimeSignals[$Command] = $Time } else{ $Script:TimeSignals.Add($Command, $Time) } } else{ if($Script:TimeSignals.ContainsKey($Command)) { $TimeSpan = New-TimeSpan -End $Time -Start (($Script:TimeSignals)[$Command]) Write-PSFMessage -Level Verbose -Message "Total time spent inside the function was $TimeSpan" -Target $TimeSpan -FunctionName $Command -Tag "TimeSignal" $Script:TimeSignals.Remove($Command) } else { Write-PSFMessage -Level Verbose -Message "The command '$Command' was never started to take part in time measurement." } } } <# .SYNOPSIS Create a new folder with datetime in its name .DESCRIPTION Create a new folder with current date and time as part of its name .PARAMETER Path Path to the parent folder where you want the new folder created .PARAMETER NoCreate Instruct the cmdlet not to create the folder .EXAMPLE PS C:\> New-FolderWithDateTime -Path "c:\temp\ax2012.tools" This will create a new folder with the current date and time as a child to the "c:\temp\ax2012.tools" folder. .NOTES Author: M�tz Jensen (@Splaxi) #> Function New-FolderWithDateTime { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 1)] [string] $Path, [Parameter(Mandatory = $false, Position = 2)] [switch] $NoCreate ) $dateString = (Get-Date).ToString("yyyy-MM-dd_HH.mm.ss") $backUpPath = Join-Path $Path $dateString Write-PSFMessage -Level Verbose -Message "Creating the new directory: $backUpPath" -Target $backUpPath if (-not ($NoCreate)) { $null = New-Item -Path $backUpPath -ItemType Directory -Force } $backUpPath } <# .SYNOPSIS Test accessible to the configuration storage .DESCRIPTION Test if the desired configuration storage is accessible with the current user context .PARAMETER ConfigStorageLocation Parameter used to instruct where to store the configuration objects The default value is "User" and this will store all configuration for the active user Valid options are: "User" "System" "System" will store the configuration so all users can access the configuration objects .EXAMPLE PS C:\> Test-ConfigStorageLocation -ConfigStorageLocation "System" This will test if the current executing user has enough privileges to save to the system wide configuration storage. The system wide configuration storage requires administrator rights. .NOTES Author: M�tz Jensen (@Splaxi) #> function Test-ConfigStorageLocation { [CmdletBinding()] [OutputType('System.String')] param ( [ValidateSet('User', 'System')] [string] $ConfigStorageLocation = "User" ) $configScope = "UserDefault" if ($ConfigStorageLocation -eq "System") { if ($Script:IsAdminRuntime) { $configScope = "SystemDefault" } else { Write-PSFMessage -Level Host -Message "Unable to locate save the <c='em'>configuration objects</c> in the <c='em'>system wide configuration store</c> on the machine. Please start an elevated session and run the cmdlet again." Stop-PSFFunction -Message "Elevated permissions needed. Please start an elevated session and run the cmdlet again." -StepsUpward 1 return } } $configScope } <# .SYNOPSIS Test multiple paths .DESCRIPTION Easy way to test multiple paths for public functions and have the same error handling .PARAMETER Path Array of paths you want to test They have to be the same type, either file/leaf or folder/container .PARAMETER Type Type of path you want to test Either 'Leaf' or 'Container' .PARAMETER Create Instruct the cmdlet to create the directory if it doesn't exist .PARAMETER ShouldNotExist Instruct the cmdlet to return true if the file doesn't exists .PARAMETER DontBreak Instruct the cmdlet NOT to break execution whenever the test condition normally should .EXAMPLE PS C:\> Test-PathExists "c:\temp","c:\temp\dir" -Type Container This will test if the mentioned paths (folders) exists and the current context has enough permission. .NOTES Author: M�tz Jensen (@splaxi) #> function Test-PathExists { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $True, Position = 1 )] [AllowEmptyString()] [string[]] $Path, [ValidateSet('Leaf', 'Container')] [Parameter(Mandatory = $True, Position = 2 )] [string] $Type, [switch] $Create, [switch] $ShouldNotExist, [switch] $DontBreak ) $res = $false $arrList = New-Object -TypeName "System.Collections.ArrayList" foreach ($item in $Path) { if([string]::IsNullOrEmpty($item)) { Stop-PSFFunction -Message "Stopping because path was either null or empty string." -StepsUpward 1 return } Write-PSFMessage -Level Verbose -Message "Testing the path: $item" -Target $item $temp = Test-Path -Path $item -Type $Type if ((-not $temp) -and ($Create) -and ($Type -eq "Container")) { Write-PSFMessage -Level Verbose -Message "Creating the path: $item" -Target $item $null = New-Item -Path $item -ItemType Directory -Force -ErrorAction Stop $temp = $true } elseif ($ShouldNotExist) { Write-PSFMessage -Level Verbose -Message "The should NOT exists: $item" -Target $item } elseif (-not $temp ) { Write-PSFMessage -Level Host -Message "The <c='em'>$item</c> path wasn't found. Please ensure the path <c='em'>exists</c> and you have enough <c='em'>permission</c> to access the path." } $null = $arrList.Add($temp) } if ($arrList.Contains($false) -and (-not $ShouldNotExist)) { if (-not $DontBreak) { Stop-PSFFunction -Message "Stopping because of missing paths." -StepsUpward 1 } } elseif ($arrList.Contains($true) -and $ShouldNotExist) { if (-not $DontBreak) { Stop-PSFFunction -Message "Stopping because file exists." -StepsUpward 1 } } else { $res = $true } $res } <# .SYNOPSIS Test if a given registry key exists or not .DESCRIPTION Test if a given registry key exists in the path specified .PARAMETER Path Path to the registry hive and sub directories you want to work against .PARAMETER Name Name of the registry key that you want to test for .EXAMPLE PS C:\> Test-RegistryValue -Path "HKLM:\SOFTWARE\Microsoft\Dynamics\Deployment\" -Name "InstallationInfoDirectory" This will query the LocalMachine hive and the sub directories "HKLM:\SOFTWARE\Microsoft\Dynamics\Deployment\" for a registry key with the name of "InstallationInfoDirectory". .NOTES Author: M�tz Jensen (@Splaxi) #> Function Test-RegistryValue { [OutputType('System.Boolean')] param( [Parameter(Mandatory = $true)] [string]$Path, [Parameter(Mandatory = $true)] [string]$Name ) if (Test-Path -Path $Path -PathType Any) { $null -ne (Get-ItemProperty $Path).$Name } else { $false } } <# .SYNOPSIS Test PSBoundParameters whether or not to support TrustedConnection .DESCRIPTION Test callers PSBoundParameters (HashTable) for details that determines whether or not a SQL Server connection should support TrustedConnection or not .PARAMETER Inputs HashTable ($PSBoundParameters) with the parameters from the callers invocation .EXAMPLE PS C:\> $UseTrustedConnection = Test-TrustedConnection $PSBoundParameters This will send the entire HashTable from the callers invocation, containing all explicit defined parameters to be analyzed whether or not the SQL Server connection should support TrustedConnection or not. .NOTES Author: M�tz Jensen (@splaxi) #> function Test-TrustedConnection { [CmdletBinding()] [OutputType([System.Boolean])] param ( [HashTable] $Inputs ) if (($Inputs.ContainsKey("SqlUser")) -or ($Inputs.ContainsKey("SqlPwd"))) { Write-PSFMessage -Level Verbose -Message "Not capable of using Trusted Connection based on supplied SQL login details." $false } elseif ($Inputs.ContainsKey("TrustedConnection")) { Write-PSFMessage -Level Verbose -Message "The script was calling with TrustedConnection directly. This overrides all other logic in respect that the caller should know what it is doing. Value was: $($Inputs.TrustedConnection)" -Tag $Inputs.TrustedConnection $Inputs.TrustedConnection } else { $false } } <# .SYNOPSIS Update all the active variables .DESCRIPTION All PSF configuration entries that are names *.active* will be extracted to their corresponding $script:VARIABLENAMe .EXAMPLE PS C:\> Update-ActiveVariables This will update all the variables that maps directly to a PSF configuration value in the *.active* part of the configuration store. .NOTES Author: M�tz Jensen (@Splaxi) #> function Update-ActiveVariables { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding()] param () foreach ($item in (Get-PSFConfig -FullName ax2012.tools.active*)) { $nameTemp = $item.FullName -replace "^ax2012.tools.", "" $name = ($nameTemp -Split "\." | ForEach-Object { (Get-Culture).TextInfo.ToTitleCase($_) } ) -Join "" New-Variable -Name $name -Value $item.Value -Scope Script -Force } if ([System.String]::IsNullOrEmpty(($Script:ActiveAosDatabaseserver -replace "null", "" ))) { Write-PSFMessage -Level Verbose -Message "ActiveAosDatabaseserver was empty. Defaulting to `"localhost`"" $Script:ActiveAosDatabaseserver = "localhost" } if ([System.String]::IsNullOrEmpty(($Script:ActiveAosModelstoredatabase -replace "null", "" ))) { Write-PSFMessage -Level Verbose -Message "ActiveAosModelstoredatabase was empty. Defaulting to `"MicrosoftDynamicsAx_model`"" $Script:ActiveAosModelstoredatabase = "MicrosoftDynamicsAx_model" } if ([System.String]::IsNullOrEmpty(($Script:ActiveAosDatabase -replace "null", "" ))) { Write-PSFMessage -Level Verbose -Message "ActiveAosDatabase was empty. Defaulting to `"MicrosoftDynamicsAx`"" $Script:ActiveAosDatabase = "MicrosoftDynamicsAx" } } <# .SYNOPSIS Update the environment config variables .DESCRIPTION Update the active environment config variables that the module will use as default values .EXAMPLE PS C:\> Update-ActiveEnvironmentVariables This will update the environment variables. .NOTES Author: M�tz Jensen (@Splaxi) #> function Update-ActiveEnvironmentVariables { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] [OutputType()] param ( ) $configName = (Get-PSFConfig -FullName "ax2012.tools.active.environment.config.name").Value if (([string]::IsNullOrEmpty($configName))) { return } $configName = $configName.ToString().ToLower() if (-not ($configName -eq "")) { $configHash = Get-AxActiveEnvironmentConfig -OutputAsHashtable foreach ($item in $configHash.Keys) { if ($item -eq "name") { continue } $name = "Environment" + (Get-Culture).TextInfo.ToTitleCase($item) Write-PSFMessage -Level Verbose -Message "$name - $($configHash[$item])" -Target $configHash[$item] Set-Variable -Name $name -Value $configHash[$item] -Scope Script } } } <# .SYNOPSIS Add configuration details for an entire AX 2012 environment .DESCRIPTION Build a configuration containing all the different servers / machines that is part of any given AX 2012 environment You could register your TEST, SAT, UAT, PROD environment and easily switch between them when you want to troubleshoot or run maintenance work against them .PARAMETER Name Name of the environment that you are adding .PARAMETER AosServers Array with server names of all the servers that host an AOS instance in the specific environment .PARAMETER InstanceName Name of the instance that is used to uniquely identify the environment across multiple AOS instances .PARAMETER DatabaseServers Array with server names of all the servers that host a SQL Server database for the environment .PARAMETER Database Database name for the SQL Server database that the AOS instance(s) connects to .PARAMETER ModelstoreDatabase Database name for the SQL Server database that holds the modelstore (code) .PARAMETER SsrsServers Array with server names of all the servers that host a Sql Server Reporting Services (SSRS) instance in the specific environment .PARAMETER EpServers Array with server names of all the servers that host a SharePoint installation with corresponding Enterprise Portal components in the specific environment .PARAMETER WmdpServers Array with server names of all the servers that host an IIS installation with corresponding Warehouse Mobile Device Portal (WMDP) components in the specific environment .PARAMETER Mr2012Servers Array with server names of all the servers that host a Management Reporter 2012 instance in the specific environment .PARAMETER SsasServers Array with server names of all the servers that host a Sql Server Analysis Services (SSAS) instance in the specific environment .PARAMETER Append Instructs the cmdlet to append the different parameter values with those that might already exist in the configuration store .PARAMETER Force Instruct the cmdlet to overwrite the specified parameter values in the configuration store .PARAMETER Temporary Instruct the cmdlet to only temporarily add the environment configuration in the configuration store Great help while building the configuration and you don't want to persist the configuration on the machine .EXAMPLE PS C:\> Add-AxEnvironmentConfig -Name AXTEST -AosServers TESTAOS01 -InstanceName AXTEST -DatabaseServers TESTSQL01 -Database AXTEST -ModelstoreDatabase AXTEST_model -Temporary This adds a new environment configuration to the configuration store. The Name AXTEST is used as the name for the configuration of the environment. The InstanceName AXTEST is used as the instance name for the configuration of the environment. The server TESTAOS01 is registered as the AOS Server. The server TESTSQL01 is registered as the SQL Server. The database AXTEST is registered as the SQL Server database. The database AXTEST_model is registered as the SQL Server database for the modelstore. .NOTES Tags: Environment, Config, Configuration, Servers Author: M�tz Jensen (@Splaxi) .LINK Clear-AxActiveEnvironmentConfig .LINK Get-AxActiveEnvironmentConfig .LINK Get-AxEnvironmentConfig .LINK Remove-AxEnvironmentConfig .LINK Set-AxActiveEnvironmentConfig #> function Add-AxEnvironmentConfig { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Name, [string[]] $AosServers, [Parameter(ValueFromPipelineByPropertyName = $true)] [string] $InstanceName, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('DatabaseServer')] [string[]] $DatabaseServers, [Alias('DatabaseName')] [string] $Database, [string] $ModelstoreDatabase, [string[]] $SsrsServers, [string[]] $EpServers, [string[]] $WmdpServers, [string[]] $Mr2012Servers, [string[]] $SsasServers, [switch] $Append, [switch] $Force, [switch] $Temporary ) Write-PSFMessage -Level Verbose -Message "Testing if configuration with the name already exists or not." -Target $configurationValue if (((Get-PSFConfig -FullName "ax2012.tools.environment.*.name").Value -contains $Name) -and (-not $Force) -and (-not $Append)) { $messageString = "An environment configuration with <c='em'>$Name</c> as name <c='em'>already exists</c>. If you want to <c='em'>overwrite</c> the current configuration, please supply the <c='em'>-Force</c> parameter. If you want to <c='em'>append</c> the current configuration, please supply the <c='em'>-Append</c> parameter" Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because an environment configuration already exists with that name." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } $configName = $Name.ToLower() #The ':keys' label is used to have a continue inside the switch statement itself :keys foreach ($key in $PSBoundParameters.Keys) { $configurationValue = $PSBoundParameters.Item($key) $configurationName = $key.ToLower() $fullConfigName = "" Write-PSFMessage -Level Verbose -Message "Working on $key with $configurationValue" -Target $configurationValue switch ($key) { "Name" { $fullConfigName = "ax2012.tools.environment.$configName.name" } { "Temporary", "Force", "Append", "Verbose" -contains $_ } { continue keys } Default { $fullConfigName = "ax2012.tools.environment.$configName.$configurationName" } } if ($Append -and $key -ne "Name") { $oldValue = @(Get-PsfConfigValue -FullName $fullConfigName) $temp = @() if($null -ne $oldValue) { $temp += $oldValue } $temp += $configurationValue $configurationValue = $temp } Write-PSFMessage -Level Verbose -Message "Setting $fullConfigName to $configurationValue" -Target $configurationValue Set-PSFConfig -FullName $fullConfigName -Value $configurationValue if (-not $Temporary) { Register-PSFConfig -FullName $fullConfigName -Scope UserDefault } } } <# .SYNOPSIS Clear the active environment config .DESCRIPTION Clear the active environment config from the configuration store .PARAMETER Temporary Instruct the cmdlet to only temporarily clear the active environment configuration in the configuration store .EXAMPLE PS C:\> Clear-AxActiveEnvironmentConfig This will clear the active environment configuration from the configuration store. .NOTES Tags: Environment, Config, Configuration, Servers Author: M�tz Jensen (@Splaxi) .LINK Add-AxEnvironmentConfig .LINK Get-AxActiveEnvironmentConfig .LINK Get-AxEnvironmentConfig .LINK Remove-AxEnvironmentConfig .LINK Set-AxActiveEnvironmentConfig #> function Clear-AxActiveEnvironmentConfig { [CmdletBinding()] [OutputType()] param ( [switch] $Temporary ) $configurationName = "ax2012.tools.active.environment.config.name" Reset-PSFConfig -FullName $configurationName if (-not $Temporary) { Register-PSFConfig -FullName $configurationName -Scope UserDefault } } <# .SYNOPSIS Clear AX 2012 Client Cache Objects .DESCRIPTION Remove AX 2012 Client Cache Object files from the file system .PARAMETER ObjectType The type of cache object that you want to remove Valid options are: AUC KTI VSAssemblies .PARAMETER UserLocation Decide which user location that you want to work against Do you want to remove the cache objects from the current user or all users? Valid options are: CurrentUser AllUsers .PARAMETER ListOnly Instruct the cmdlet to only list the files that matches your selection from the other parameters .EXAMPLE PS C:\> Clear-AxClientCacheObjects -ObjectType "Auc" -UserLocation "CurrentUser" -ListOnly This will list all the Auc files under the current user location. It will work against the ObjectType "Auc". It will work againt the UserLocation "CurrentUser". It will only list the files and folders, it will NOT delete anything. .EXAMPLE PS C:\> Clear-AxClientCacheObjects -ObjectType "Auc" -UserLocation "CurrentUser" This will delete all the Auc files under the current user location. It will work against the ObjectType "Auc". It will work againt the UserLocation "CurrentUser". It WILL delete the files without further warning or notification! .EXAMPLE PS C:\> Clear-AxClientCacheObjects -ObjectType "Auc" -UserLocation "AllUsers" This will delete all the Auc files under all users locations. It will work against the ObjectType "Auc". It will work againt the UserLocation "AllUsers". It WILL delete the files without further warning or notification! .EXAMPLE PS C:\> Clear-AxClientCacheObjects -ObjectType "Auc","Kti","VSAssemblies" -UserLocation "CurrentUser" This will delete all the Auc,Kti and VSAssemblies files under the current user location. It will work against the ObjectType "Auc","Kti","VSAssemblies". It will work againt the UserLocation "CurrentUser". It WILL delete the files without further warning or notification! .NOTES Tags: Client Cache, Cache, KTI, AUC, VSAssemblies Author: M�tz Jensen (@Splaxi) All credits goes to Kenneth Madsen (@KennethGrupp) for providing detailed examples on how to achieve this the best way using powershell #> function Clear-AxClientCacheObjects { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] param ( [string[]] $ObjectType, [string] $UserLocation, [switch] $ListOnly ) Invoke-TimeSignal -Start $basePath = "" switch ($UserLocation) { "CurrentUser" { Write-PSFMessage -Level Verbose -Message "CurrentUser path was selected as the location to clear." $basePath = $env:LocalAppData } "AllUsers" { Write-PSFMessage -Level Verbose -Message "AllUsers path was selected as the location to clear." $basePath = "$env:LocalAppData" -replace "$env:username", "*" } } $pathToClear = "" foreach ($item in $ObjectType) { switch ($item.ToUpper()) { "AUC" { Write-PSFMessage -Level Verbose -Message "Working against cache object type: `"AUC`"" $pathToClear = Join-Path $basePath "*.auc" } "KTI" { Write-PSFMessage -Level Verbose -Message "Working against cache object type: `"KTI`"" $pathToClear = Join-Path $basePath "*.kti" } "VSASSEMBLIES" { Write-PSFMessage -Level Verbose -Message "Working against cache object type: `"VSAssemblies`"" $pathToClear = Join-Path $basePath "Microsoft\Dynamics Ax\VSAssemblies*\*" } } Write-PSFMessage -Level Verbose -Message "Working against path: $pathToClear" -Target $pathToClear if ($ListOnly) { Get-ChildItem -Path $pathToClear } else { Remove-Item -Path $pathToClear -Force } } Invoke-TimeSignal -End } <# .SYNOPSIS Clear AX 2012 AOS Server Cache Objects .DESCRIPTION Remove AX 2012 AOS Server Cache Object files from the file system .PARAMETER ObjectType The type of cache object that you want to remove Valid options are: XppIL Label VSAssemblies .PARAMETER InstanceName Name of the instance that you are working against Default value can be configured with the Set-AxActiveAosConfig cmdlet .PARAMETER ListOnly Instruct the cmdlet to only list the files that matches your selection from the other parameters .EXAMPLE PS C:\> Clear-AxServerCacheObjects -ObjectType "XppIL" -InstanceName "AXTEST" -ListOnly This will list all the XppIL files under the AXTEST AOS Instance location. It will work against the ObjectType "XppIL". It will work againt the InstanceName "AXTEST". It will only list the files and folders, it will NOT delete anything. .EXAMPLE PS C:\> Clear-AxServerCacheObjects -ObjectType "XppIL" -InstanceName "AXTEST" This will delete all the XppIL files under the AXTEST AOS Instance location. It will work against the ObjectType "XppIL". It will work againt the InstanceName "AXTEST". It WILL delete the files without further warning or notification! .EXAMPLE PS C:\> Clear-AxServerCacheObjects -ObjectType "XppIL","Label","VSAssemblies" -InstanceName "AXTEST" This will delete all the XppIL,Label and VSAssemblies files under the AXTEST AOS Instance location. It will work against the ObjectType "XppIL","Label","VSAssemblies". It will work againt the InstanceName "AXTEST". It WILL delete the files without further warning or notification! .NOTES Tags: Client Cache, Cache, Label, XppIL, VSAssemblies Author: M�tz Jensen (@Splaxi) All credits goes to Kenneth Madsen (@KennethGrupp) for providing detailed examples on how to achieve this the best way using powershell #> function Clear-AxServerCacheObjects { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] param ( [string[]] $ObjectType, [string] $InstanceName = $Script:ActiveAosInstancename, [switch] $ListOnly ) if ([String]::IsNullOrEmpty($InstanceName)) { $messageString = "Instance name was <c='em'>empty</c>. You either need to supply it with <c='em'>-InstanceName</c> parameter or configure it using the <c='em'>Set-AxActiveAosConfig</c> cmdlet." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because of missing instance name parameter." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } if ($InstanceName -eq "*") { $messageString = "Instance name cannot be <c='em'>*</c>. You either need to supply an instance name that will only result in <c='em'>one</c> AOS instance." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because of missing instance name parameter." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } Invoke-TimeSignal -Start $basePath = (Get-AxAosInstance -InstanceName $InstanceName).BinDirectory $parms = @{} foreach ($item in $ObjectType) { switch ($item.ToUpper()) { "XPPIL" { Write-PSFMessage -Level Verbose -Message "Working against cache object type: `"XppIL`"" $parms.Path = (Join-Path $basePath "XppIL") $parms.Include = "*.*" } "LABEL" { Write-PSFMessage -Level Verbose -Message "Working against cache object type: `"Label`"" $parms.Path = (Join-Path $basePath "Application\appl\standard\ax*.*") } "VSASSEMBLIES" { Write-PSFMessage -Level Verbose -Message "Working against cache object type: `"VSAssemblies`"" $parms.Path = (Join-Path $basePath "VSAssemblies\*.*") } } Write-PSFMessage -Level Verbose -Message "Working against path: $($parms.Path)" -Target $parms.Path if ($ListOnly) { Get-ChildItem @parms -Recurse } else { Remove-Item @parms -Recurse -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } } Invoke-TimeSignal -End } <# .SYNOPSIS Export an AX 2012 modelstore file .DESCRIPTION Export an AX 2012 modelstore file from the modelstore database .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER ModelstoreDatabase Name of the modelstore database Default value is: "MicrosoftDynamicsAx_model" Note: From AX 2012 R2 and upwards you need to provide the full name for the modelstore database. E.g. "AX2012R3_PROD_model" .PARAMETER InstanceName Name of the instance that you are working against If not supplied the cmdlet will take the name of the database and use that .PARAMETER Suffix A suffix text value that you want to add to the name of the file while it is exported The default value is: (Get-Date).ToString("yyyyMMdd") This will always name you file with the current date .PARAMETER Path Path to the location where you want the file to be exported Default value is: "c:\temp\ax2012.tools" .PARAMETER ShowOriginalProgress Instruct the cmdlet to show the standard output in the console Default is $false which will silence the standard output .PARAMETER OutputCommandOnly Instruct the cmdlet to only generate the needed command and not execute it .EXAMPLE PS C:\> Export-AxModelStoreV2 This will execute the cmdlet with all the default values. This will work against the SQL server that is on localhost. The database is expected to be "MicrosoftDynamicsAx_model". The path where the exported modelstore file will be saved is: "c:\temp\ax2012.tools". .NOTES Author: M�tz Jensen (@Splaxi) #> Function Export-AxModelStoreV2 { [CmdletBinding()] [OutputType([System.String], ParameterSetName="Generate")] Param( [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [string] $ModelstoreDatabase = $Script:ActiveAosModelstoredatabase, [string] $InstanceName = $Script:ActiveAosInstancename, [string] $Suffix = $((Get-Date).ToString("yyyyMMdd")), [string] $Path = $Script:DefaultTempPath, [switch] $ShowOriginalProgress, [Parameter(ParameterSetName = "Generate")] [switch] $OutputCommandOnly ) Invoke-TimeSignal -Start if (-not (Test-PathExists -Path $Path -Type Container -Create)) { return } if (-not (Test-PathExists -Path $Script:AxPowerShellModule -Type Leaf)) { return } if ([System.String]::IsNullOrEmpty($InstanceName)) { $InstanceName = "{0}" -f $ModelstoreDatabase.Replace("_model", "") } if (-not ([system.string]::IsNullOrEmpty($Suffix))) { $ExportPath = Join-Path $Path "$($InstanceName)_$($Suffix).axmodelstore" } else { $ExportPath = Join-Path $Path "$InstanceName.axmodelstore" } $params = @{ Server = $DatabaseServer Database = $ModelstoreDatabase File = $ExportPath } if ($OutputCommandOnly) { $arguments = Convert-HashToArgString -InputObject $params "Export-AxModelStore $($arguments -join ' ')" } else { Write-PSFMessage -Level Verbose -Message "Starting the export of the model store" $null = Import-Module $Script:AxPowerShellModule $outputRes = Export-AxModelStore @params if ($ShowOriginalProgress) { $outputRes } Clear-Ax2012StandardPowershellModule } Invoke-TimeSignal -End } <# .SYNOPSIS Export AX 2012 model .DESCRIPTION Export AX 2012 model from the AX 2012 model store .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER ModelstoreDatabase Name of the modelstore database Default value is: "MicrosoftDynamicsAx_model" Note: From AX 2012 R2 and upwards you need to provide the full name for the modelstore database. E.g. "AX2012R3_PROD_model" .PARAMETER Path Path to the location where you want the file to be exported Default value is: "c:\temp\ax2012.tools" .PARAMETER Name Name of the AX 2012 model that you are looking for Accepts wildcards for searching. E.g. -Name "ISV*MODULE*" Default value is "*" which will search for all models .PARAMETER Id Id of the AX 2012 model that you are looking for Accepts wildcards for searching. E.g. -Id "2*" Default value is "*" which will search for all models .PARAMETER Layer Layer where the AX 2012 model that you are looking for should reside Accepts wildcards for searching. E.g. -Layer "IS*" Default value is "*" which will search for models in all layers .PARAMETER ShowOriginalProgress Instruct the cmdlet to show the standard output in the console Default is $false which will silence the standard output .PARAMETER OutputCommandOnly Instruct the cmdlet to output the script to execute the command in hand .EXAMPLE PS C:\> Get-AxAosInstance | Export-AxModelV2 This will fetch all the AX 2012 AOS instances that are configured on the machine. Foreach of the instances it will export all AX 2012 Models into a sub folder to "c:\temp\ax2012.tools". .EXAMPLE PS C:\> Export-AxModelV2 -DatabaseServer localhost -ModelstoreDatabase MicrosoftDynamicsAx_model -Name *CUS* This will fetch all the AX 2012 AOS instances that are configured on the machine. Foreach of the instances it will export all AX 2012 Models into a sub folder to "c:\temp\ax2012.tools". .NOTES Author: M�tz Jensen (@Splaxi) #> Function Export-AxModelV2 { [CmdletBinding()] [OutputType([System.String], ParameterSetName="Generate")] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [Parameter(ValueFromPipelineByPropertyName = $true)] [string] $ModelstoreDatabase = $Script:ActiveAosModelstoredatabase, [string] $Path = $Script:DefaultTempPath, [string] $Name = "*", [string] $Id = "*", [string] $Layer = "*", [switch] $ShowOriginalProgress, [Parameter(ParameterSetName = "Generate")] [switch] $OutputCommandOnly ) BEGIN { if (-not ($OutputCommandOnly)) { if (-not (Test-PathExists -Path $Path -Type Container -Create)) { return } $backupFilePath = New-FolderWithDateTime -Path $Path } else { $backupFilePath = New-FolderWithDateTime -Path $Path -NoCreate } $null = Import-Module $Script:AxPowerShellModule } PROCESS { Invoke-TimeSignal -Start $xml = (Get-AXModel -Server $DatabaseServer -Database $ModelstoreDatabase | ConvertTo-Xml ) $nodes = $xml.SelectNodes("Objects/Object") foreach ($obj in $nodes) { $filenameAxModel = "" $modelId = "" $modelLayer = "" $modelName = "" # Loop all properties foreach ($property in $obj.SelectNodes("Property")) { if ($property.GetAttribute("Name").Equals( "Name" )) { $modelName = $property.InnerText } if ($property.GetAttribute("Name").Equals( "Layer" )) { $modelLayer = $property.InnerText } if ($property.GetAttribute("Name").Equals( "ModelId" )) { $modelId = $property.InnerText } } $filenameAxModel = $modelId + "_" + $modelName + ".axmodel" Write-PSFMessage -Level Verbose -Message "Testing that ModelName, ModelId and Layer matches the search criteria" if ($modelName -NotLike $Name) { continue } if ($modelId -NotLike $Id) { continue } if ($modelLayer -NotLike $Layer) { continue } if ($Script:LayerDictionary.ContainsKey($modelLayer.ToUpper()) -and $filenameAxModel -ne "") { $localLayer = $Script:LayerDictionary.Get_Item($modelLayer.ToUpper()) + $modelLayer.ToUpper() $tempPath = Join-Path $backupFilePath $localLayer $filenameAxModel = Join-Path $tempPath $filenameAxModel $params = @{Model = $modelName; File = $filenameAxModel; Server = $DatabaseServer; Database = $ModelstoreDatabase } if ($OutputCommandOnly) { $arguments = Convert-HashToArgString -InputObject $params "Export-AXModel $($arguments -join ' ')" } else { if (-not (Test-PathExists -Path $tempPath -Type Container -Create)) { return } Write-PSFMessage -Level Verbose -Message "Starting the export of the ax model file" $outputRes = Export-AXModel @params if ($ShowOriginalProgress) { $outputRes } } } else { Write-PSFMessage -Level Verbose -Message "Skipping $filenameAxModel in layer $modelLayer" } } Invoke-TimeSignal -End } END { if (-not ($OutputCommandOnly)) { [PSCustomObject]@{ Path = $backupFilePath } } Clear-Ax2012StandardPowershellModule } } <# .SYNOPSIS Get the active stored AX 2012 AOS configuration .DESCRIPTION Get the active AX 2012 AOS configuration from the configuration storage .EXAMPLE PS C:\> Get-AxActiveAosConfig This will export all the stored details saved into the configuration storage. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxActiveAosConfig { [CmdletBinding()] param ( ) $res = [Ordered]@{} $res.InstanceName = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.instancename' $res.BinDirectory = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.bindirectory' $res.DatabaseServer = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.databaseserver' $res.DatabaseName = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.database' $res.ModelstoreDatabase = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.modelstoredatabase' $res.AosPort = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.aos.port' $res.WsdlPort = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.wsdl.port' $res.NetTcpPort = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.nettcp.port' $res.InstanceNumber = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.instance.number' $res.ComputerName = Get-PSFConfigValue -FullName 'ax2012.tools.active.aos.computername' [PSCustomObject] $res } <# .SYNOPSIS Get the active environment configuration .DESCRIPTION Get the active environment configuration from the configuration store .PARAMETER OutputAsHashtable Instruct the cmdlet to return a hashtable object .PARAMETER EnableException This parameters disables user-friendly warnings and enables the throwing of exceptions This is less user friendly, but allows catching exceptions in calling scripts .EXAMPLE PS C:\> Get-AxActiveEnvironmentConfig This will get the active environment configuration. .NOTES Tags: Environment, Config, Configuration, Servers Author: M�tz Jensen (@Splaxi) .LINK Add-AxEnvironmentConfig .LINK Get-AxEnvironmentConfig .LINK Set-AxActiveEnvironmentConfig #> function Get-AxActiveEnvironmentConfig { [CmdletBinding()] [OutputType()] param ( [switch] $OutputAsHashtable, [switch] $EnableException ) $configName = (Get-PSFConfig -FullName "ax2012.tools.active.environment.config.name").Value if ($configName -eq "") { $messageString = "It looks like there <c='em'>isn't configured</c> an active environment configuration." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because an active environment configuration wasn't found." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } Get-AxEnvironmentConfig -Name $configName -OutputAsHashtable:$OutputAsHashtable } <# .SYNOPSIS Get AX 2012 AOS Instance .DESCRIPTION Get AX 2012 AOS Instance details from the local machine .PARAMETER Name The search string to filter the AOS instance that you're looking for The parameter supports wildcards. E.g. -Name "*DEV*" Default value is "*" and will give you all the instances .PARAMETER InstanceNo The search string to filter the AOS instance that you're looking for The parameter supports wildcards. E.g. -InstanceNo "*1*" Default value is "*" and will give you all the instances .EXAMPLE PS C:\> Get-AxAosInstance This will get you all the installed AX 2012 AOS instances on the machine .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxAosInstance { [CmdletBinding()] Param( [Alias('InstanceName')] [string] $Name = "*", [string] $InstanceNo = "*" ) $Instances = Get-ChildItem -Path $Script:RegistryAos $res = New-Object System.Collections.ArrayList $Instances | ForEach-Object { $null = $res.Add((Get-AxAosInstanceDetails $_.Name)) } foreach ($obj in $res) { if ($obj.InstanceName -NotLike $Name) { continue } if ($obj.InstanceNumber -NotLike $InstanceNo) { continue } $obj } } <# .SYNOPSIS Get AOS server details from the SQL Server database .DESCRIPTION Traverse the SysServerConfig table from the AX 2012 database and get all registered AOS Servers .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER DatabaseName Name of the database Default value is: "MicrosoftDynamicsAx" .PARAMETER SqlUser User name of the SQL Server credential that you want to use when working against the database .PARAMETER SqlPwd Password of the SQL Server credential that you want to use when working against the database .EXAMPLE PS C:\> Get-AxAosServerFromDB This will query the "MicrosoftDynamicsAx" hosted on the localhost server. It will extract all AOS Servers from the SysServerConfig table. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxAosServerFromDB { [CmdletBinding()] #[OutputType()] param ( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 1)] [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 2)] [string] $DatabaseName = $Script:ActiveAosDatabase, [Parameter(Mandatory = $false, Position = 3)] [string] $SqlUser, [Parameter(Mandatory = $false, Position = 4)] [string] $SqlPwd ) Invoke-TimeSignal -Start $baseParams = Get-DeepClone $PSBoundParameters $baseParams.Add("TrustedConnection", $true) $UseTrustedConnection = Test-TrustedConnection $baseParams $SqlParams = @{ DatabaseServer = $DatabaseServer; DatabaseName = $DatabaseName; SqlUser = $SqlUser; SqlPwd = $SqlPwd } $sqlCommand = Get-SqlCommand @SqlParams -TrustedConnection $UseTrustedConnection $sqlCommand.CommandText = (Get-Content "$script:ModuleRoot\internal\sql\get-aosserversfromdatabase.sql") -join [Environment]::NewLine try { Write-PSFMessage -Level InternalComment -Message "Executing a script against the database." -Target (Get-SqlString $sqlCommand) $sqlCommand.Connection.Open() $reader = $sqlCommand.ExecuteReader() while ($reader.Read() -eq $true) { $rawServer = "$($reader.GetString($($reader.GetOrdinal("SERVERID"))))" [PSCustomObject]@{ RawServerName = $rawServer InstanceNumber = $rawServer.ToString().Split("@")[0] ServerName = $rawServer.ToString().Split("@")[1] IsBatchEnabled = [bool][int]"$($reader.GetInt32($($reader.GetOrdinal("IsBatch"))))" } } } catch { Write-PSFMessage -Level Host -Message "Something went wrong while working against the database" -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of errors" return } finally { if ($sqlCommand.Connection.State -ne [System.Data.ConnectionState]::Closed) { $sqlCommand.Connection.Close() } $sqlCommand.Dispose() } Invoke-TimeSignal -End } <# .SYNOPSIS Get the build numbers .DESCRIPTION Get the build numbers for the AX 2012 client .PARAMETER Path The path to the Ax32.exe file you want to work against The default path is read from the registry .EXAMPLE PS C:\> Get-AxClientBuild This will get the executable path and the build numbers for the client. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxClientBuild { [CmdletBinding()] Param( [string] $Path = $(Join-Path $Script:ClientBin "Ax32.exe") ) $BuildNumbers = Get-FileVersion -Path $Path $clientDetails = [Ordered]@{} $clientDetails.ExecutablePath = $Path $clientDetails.FileVersion = $BuildNumbers.FileVersion $clientDetails.ProductVersion = $BuildNumbers.ProductVersion $clientDetails.FileVersionUpdated = $BuildNumbers.FileVersionUpdated $clientDetails.ProductVersionUpdated = $BuildNumbers.ProductVersionUpdated [PSCustomObject] $clientDetails } <# .SYNOPSIS Get the AX 2012 client configuration .DESCRIPTION Get the AX 2012 client configuration from the registry .PARAMETER Name Name of the configuration that you are looking for The parameter supports wildcards. E.g. -Name "*DEV*" .EXAMPLE PS C:\> Get-AxClientConfig This will get all available client configurations from the registry and display them. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxClientConfig { [CmdletBinding()] Param( [Parameter()] [SupportsWildcards()] [string] $Name = "*" ) $configs = Get-ChildItem -Path $Script:RegistryClientConfig foreach ($item in $configs) { $RegistryPath = Get-Item -Path $($item.Name.Replace("HKEY_CURRENT_USER", "HKCU:")) $configName = Split-Path -Path $RegistryPath.Name -Leaf if ($configName -NotLike $Name) { continue } $RegInner = Get-ItemProperty -Path $($RegistryPath.Name.Replace("HKEY_CURRENT_USER", "HKCU:")) $res = [Ordered]@{ConfigName = $configName} $res.AosServer = ($RegInner.Aos2).Split(":")[0] $res.AosPort = ($RegInner.Aos2).Split(":")[1] if ($RegInner.Aos2 -like "*@*") { $res.InstanceName = ($RegInner.Aos2).Split("@")[0] } $res.AosTrafficEncrypted = $RegInner.AosEncryption $res.BinDir = $RegInner.BinDir $res.ClientDirectory = $RegInner.Directory $res.Aol = $RegInner.Aol $res.AolCode = $RegInner.AolCode [PSCustomObject]$res } } <# .SYNOPSIS Get AX 2012 environment details from the configuration store .DESCRIPTION Get the environment details for the AX 2012 environment(s) that are stored in the configuration store .PARAMETER Name Name of the configuration that you want to work against .PARAMETER OutputAsHashtable Instruct the cmdlet to return a hastable object .PARAMETER EnableException This parameters disables user-friendly warnings and enables the throwing of exceptions This is less user friendly, but allows catching exceptions in calling scripts .EXAMPLE PS C:\> Get-AxEnvironmentConfig This will get all saved environment configurations. .NOTES Tags: Servicing, Environment, Config, Configuration, Servers Author: M�tz Jensen (@Splaxi) .LINK Add-AxEnvironmentConfig .LINK Get-AxActiveEnvironmentConfig .LINK Set-AxActiveEnvironmentConfig #> function Get-AxEnvironmentConfig { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')] [CmdletBinding()] [OutputType('PSCustomObject')] param ( [string] $Name = "*", [switch] $OutputAsHashtable, [switch] $EnableException ) Write-PSFMessage -Level Verbose -Message "Fetch all configurations based on $Name" -Target $Name $Name = $Name.ToLower() $configurations = Get-PSFConfig -FullName "ax2012.tools.environment.$Name.name" if($($configurations.count) -lt 1) { $messageString = "No configurations found <c='em'>with</c> the name." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because no configuration found." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>',''))) return } foreach ($configName in $configurations.Value.ToLower()) { Write-PSFMessage -Level Verbose -Message "Working against the $configName configuration" -Target $configName $res = @{} $configName = $configName.ToLower() foreach ($config in Get-PSFConfig -FullName "ax2012.tools.environment.$configName.*") { Write-PSFMessage -Level Verbose -Message "Working against the $($config.FullName.ToString()) of $configName configuration" -Target $configName $propertyName = (Get-Culture).TextInfo.ToTitleCase($config.FullName.ToString().Replace("ax2012.tools.environment.$configName.", "")).Replace("servers","Servers") $res.$propertyName = $config.Value } if($OutputAsHashtable) { $res } else { [PSCustomObject]$res | Select-PSFObject -TypeName "AX2012.TOOLS.Environment.Config" } } } <# .SYNOPSIS Get the registered details for Azure Logic App .DESCRIPTION Get the details that are stored for the module when it has to invoke the Azure Logic App .EXAMPLE PS C:\> Get-AxLogicAppConfig This will fetch the current registered Azure Logic App details on the machine. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxLogicAppConfig { [CmdletBinding()] param () [PSCustomObject][ordered]@{ Email = (Get-PSFConfigValue -Fullname 'ax2012.tools.active.logicapp.email') URL = (Get-PSFConfigValue -Fullname 'ax2012.tools.active.logicapp.url') Subject = (Get-PSFConfigValue -Fullname 'ax2012.tools.active.logicapp.subject') } } <# .SYNOPSIS Get Management Reporter 2012 Instance .DESCRIPTION Get Management Reporter 2012 Instance details from the local machine .EXAMPLE PS C:\> Get-AxMr2012Instance This will output all the core Management Reporter details on the machine. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxMr2012Instance { [CmdletBinding()] Param() $RegistryPath = $Script:RegistryMr2012 $RegKey = Get-Item -Path $RegistryPath.Replace("HKEY_LOCAL_MACHINE", "HKLM:") $RegOuter = Get-ItemProperty -Path ($RegKey.Name).Replace("HKEY_LOCAL_MACHINE", "HKLM:") $res = [Ordered]@{ } $res.BinDirectory = Join-Path $RegOuter.InstallLocation Server if (Test-PathExists -Path $Script:Mr2012DeploymentLogsPath -Type Container) { $res.DeploymentLogsPath = $Script:Mr2012DeploymentLogsPath } $configPath = Join-Path $res.BinDirectory $Script:Mr2012ConfigPath $serviceConfigPath = Join-Path $res.BinDirectory $Script:Mr2012ServiceConfigPath if (-not (Test-PathExists -Path $configPath, $serviceConfigPath -Type Leaf)) { return } if (Test-PSFFunctionInterrupt) { return } $configXmlString = Get-Content -Path $configPath $configXmlString -split ";" | ForEach-Object { $tempArray = $_.ToString().Split("=") switch -Wildcard ($_) { "*Data Source=*" { $res.DatabaseServer = $tempArray[$tempArray.Length - 1] } "*Initial Catalog=*" { $res.Database = $tempArray[$tempArray.Length - 1] } } } $serviceConfigXmlString = Get-Content -Path $serviceConfigPath $serviceConfigXmlString -split "<" | ForEach-Object { $tempArray = $_.ToString().Split("=") switch -Wildcard ($_) { "*DefaultHttpBaseAddress*" { $tempString = $tempArray[$tempArray.Length - 1] $res.HttpAddress = $tempString.Substring(0, $tempString.LastIndexOf('"')) -replace '"', '' } } } [PSCustomObject]$res | Select-PSFObject -TypeName "AX2012.TOOLS.Mr2012.Config" } <# .SYNOPSIS Get the parameter sniffing configuration .DESCRIPTION Get the parameter sniffing value from the database that has been released by Microsoft for AX 2012 .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER DatabaseName Name of the database Default value is: "MicrosoftDynamicsAx" .PARAMETER SqlUser User name of the SQL Server credential that you want to use when working against the database .PARAMETER SqlPwd Password of the SQL Server credential that you want to use when working against the database .EXAMPLE PS C:\> Get-AxParameterSniffingSetting This will query the database for the parameter sniffing settings. The result will be displayed along with a ShouldBe value to easily tell you if something is off. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxParameterSniffingSetting { [CmdletBinding()] #[OutputType()] param ( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 1)] [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 2)] [string] $DatabaseName = $Script:ActiveAosDatabase, [Parameter(Mandatory = $false, Position = 3)] [string] $SqlUser, [Parameter(Mandatory = $false, Position = 4)] [string] $SqlPwd ) Invoke-TimeSignal -Start $baseParams = Get-DeepClone $PSBoundParameters $baseParams.Add("TrustedConnection", $true) $UseTrustedConnection = Test-TrustedConnection $baseParams $SqlParams = @{ DatabaseServer = $DatabaseServer; DatabaseName = $DatabaseName; SqlUser = $SqlUser; SqlPwd = $SqlPwd } $sqlCommand = Get-SqlCommand @SqlParams -TrustedConnection $UseTrustedConnection $sqlCommand.CommandText = (Get-Content "$script:ModuleRoot\internal\sql\get-axparametersniffingsetting.sql") -join [Environment]::NewLine try { Write-PSFMessage -Level InternalComment -Message "Executing a script against the database." -Target (Get-SqlString $sqlCommand) $sqlCommand.Connection.Open() $reader = $sqlCommand.ExecuteReader() while ($reader.Read() -eq $true) { [PSCustomObject]@{ Name = "$($reader.GetString($($reader.GetOrdinal("Name"))))" Value = "$($reader.GetString($($reader.GetOrdinal("Value"))))" ShouldBe = "1" } } } catch { Write-PSFMessage -Level Host -Message "Something went wrong while working against the database" -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of errors" return } finally { if ($sqlCommand.Connection.State -ne [System.Data.ConnectionState]::Closed) { $sqlCommand.Connection.Close() } $sqlCommand.Dispose() } Invoke-TimeSignal -End } <# .SYNOPSIS Get hash values for XppIL files .DESCRIPTION Generate the file hash values for each *.netmodule & *.dll file in the XppIL folder .PARAMETER InstanceName Name of the instance that you are working against Default value can be configured with the Set-AxActiveAosConfig cmdlet .PARAMETER OutputPath Path to the folder where the output file must be saved Default value is: "C:\temp\ax2012.tools" .PARAMETER FileName Name of the output as you want it to be named If left empty the output file will be named based on the following pattern: "SERVERNAME_INSTANCENAME_XppIL_HashValue.txt" .EXAMPLE PS C:\> Get-AxServerXppILHash -InstanceName "AXTEST" This will generate the file hash values for the XppIL files for the AXTEST AOS Instance. It will work against the Instance AXTEST. It will save the output file to the default folder location "C:\temp\ax2012.tools". The file will named "SERVER_AXTEST_XppIL_HashValues.txt" .EXAMPLE PS C:\> Get-AxServerXppILHash This will generate the file hash values for the XppIL files for the default AOS Instance. It will work against the default AOS Instance that has been configured with Set-AxActiveAosConfig. It will save the output file to the default folder location "C:\temp\ax2012.tools". The file will named "SERVER_INSTANCENAME_XppIL_HashValues.txt" The default value of the instance name can be configured with the Set-AxActiveAosConfig cmdlet. .NOTES Tags: XppIL, Hash, Files Author: M�tz Jensen (@Splaxi) All credits goes to Kenneth Madsen (@KennethGrupp) for providing detailed examples on how to achieve this the best way using powershell #> function Get-AxServerXppILHash { [CmdletBinding()] param ( [string] $InstanceName = $Script:ActiveAosInstancename, [string] $OutputPath = $Script:DefaultTempPath, [string] $FileName ) if ([String]::IsNullOrEmpty($InstanceName)) { $messageString = "Instance name was <c='em'>empty</c>. You either need to supply it with <c='em'>-InstanceName</c> parameter or configure it using the <c='em'>Set-AxActiveAosConfig</c> cmdlet." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because of missing instance name parameter." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } if ($InstanceName -eq "*") { $messageString = "Instance name cannot be <c='em'>*</c>. You either need to supply an instance name that will only result in <c='em'>one</c> AOS instance." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because of missing instance name parameter." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } if (-not (Test-PathExists -Path $Path -Type Container -Create)) { return } Invoke-TimeSignal -Start if ([String]::IsNullOrEmpty($FileName)) { $filename = "$($env:COMPUTERNAME)_$InstanceName`_XppIL_HashValues.txt" } $outputFile = Join-Path $OutputPath $FileName Write-PSFMessage -Level Verbose -Message "Removing old output file: $outputFile" -Target $outputFile Remove-Item -Path $outputFile -Force -WarningAction SilentlyContinue -ErrorAction SilentlyContinue $basePath = (Get-AxAosInstance -InstanceName $InstanceName).BinDirectory $fileExtensions = @("*.netmodule", "*.dll") $searchPath = (Join-Path $basePath "XppIL") Write-PSFMessage -Level Verbose -Message "Working against the $InstanceName instance with the path: $searchPath" -Target $searchPath $xppFiles = Get-ChildItem -Path $searchPath -Include $fileExtensions -Recurse $resList = New-Object System.Collections.Generic.List[object] foreach ($item in $xppFiles) { $resList.Add($(Get-FileHash -Path $item.FullName)) } Write-PSFMessage -Level Verbose -Message "Generating the hash values and saving the output to path: $outputFile" -Target $outputFile $resList.ToArray() | Sort-Object Path | Format-Table Hash, Path -Wrap -AutoSize | Out-String -Width 4000 | Out-File $outputFile -Encoding utf8 Invoke-TimeSignal -End } <# .SYNOPSIS Get the status of the AX 2012 service(s) .DESCRIPTION Get the status of AX 2012 service(s) on the computer .PARAMETER ComputerName Name of the computer(s) that you want to work against .PARAMETER All Instruct the cmdlet to include all known AX 2012 services .PARAMETER AosInstanceName Name of the AOS instance that you are looking for Accepts wildcards for searching. E.g. -AosInstanceName "*DEV*" If AxActiveAosConfiguration has been configured, the default value is the name of the instance registered Default value is otherwise "*" which will search for all AOS instances .PARAMETER Aos Instruct the cmdlet to include the AOS service .PARAMETER ManagementReporter Instruct the cmdlet to include the ManagementReporter service .PARAMETER DIXF Instruct the cmdlet to include the DIXF service .PARAMETER ScanAllAosServices Instruct the cmdlet to look for all available AOS Instances on the computer .EXAMPLE PS C:\> Get-AxService This will get the status for all the default services from the local computer. If AxActiveAosConfiguration has been configured, it will work against the ComputerName and AosInstanceName registered. .EXAMPLE PS C:\> Get-AxService -ScanAllAosServices This will scan for all available AOS Services. If AxActiveAosConfiguration has been configured, it will work against the ComputerName registered otherwise localhost is used. .EXAMPLE PS C:\> Get-AxService -ComputerName TEST-AOS-01 -Aos This will get all AOS instances (services) from the server named "TEST-AOS-01". If AxActiveAosConfiguration has been configured, it will work against the AosInstanceName registered otherwise it will find all. .EXAMPLE PS C:\> Get-AxService -ComputerName TEST-AOS-01 -Aos -AosInstanceName *DEV* This will get all AOS instances (services) that match the search pattern "*DEV*" from the server named "TEST-AOS-01". .EXAMPLE PS C:\> Get-AxService -ComputerName TEST-AOS-01 -Aos | Start-AxService -ShowOriginalProgress This will scan the "TEST-AOS-01" server for all AOS instances (services) and start them. It will show the status for the service(s) on the server afterwards. If AxActiveAosConfiguration has been configured, it will work against the AosInstanceName registered otherwise it will find all. .EXAMPLE PS C:\> Get-AxService -ComputerName TEST-AOS-01 -Aos | Stop-AxService -ShowOriginalProgress This will scan the "TEST-AOS-01" server for all AOS instances (services) and stop them. It will show the status for the service(s) on the server afterwards. If AxActiveAosConfiguration has been configured, it will work against the AosInstanceName registered otherwise it will find all. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxService { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Alias('ServerName')] [Parameter(ValueFromPipelineByPropertyName = $true)] [string[]] $ComputerName = $Script:ActiveAosComputername, [Parameter(ParameterSetName = 'Default')] [switch] $All = $true, [Alias('InstanceName')] [string] $AosInstanceName = $(if (-not ([System.String]::IsNullOrEmpty($Script:ActiveAosInstancename))) { "*$Script:ActiveAosInstancename" } else { "*" }), [Parameter(ParameterSetName = 'Specific')] [switch] $Aos, [Parameter(ParameterSetName = 'Specific')] [switch] $ManagementReporter, [Parameter(ParameterSetName = 'Specific')] [switch] $DIXF, [switch] $ScanAllAosServices ) begin { if ($PSCmdlet.ParameterSetName -eq "Specific") { $All = $false } if (!$All -and !$Aos -and !$ManagementReporter -and !$DIXF) { Write-PSFMessage -Level Host -Message "You have to use at least one switch when running this cmdlet. Please run the cmdlet again." Stop-PSFFunction -Message "Stopping because of missing parameters" return } $baseParams = Get-DeepClone $PSBoundParameters $params = @{ } $includeParamNames = @("ManagementReporter", "DIXF") foreach ($key in $baseParams.Keys) { if ($includeParamNames -notcontains $key ) { continue } Write-PSFMessage -Level Verbose -Message "Working on key: $key" -Target $key $null = $params.Add($key, $true) } if ($params.Count -eq 0) { if ($All) { $params.AllAxServices = $true $Services = @(Get-ServiceList @params) } } else { $Services = @(Get-ServiceList @params) } if ($PSBoundParameters.ContainsKey("Aos")) { Write-PSFMessage -Level Verbose -Message "Aos seems to be bound" -Target $key $searchServicesAos = @(Get-ServiceList -Aos) } } process { if (Test-PSFFunctionInterrupt) { return } $res = New-Object System.Collections.ArrayList foreach ($server in $ComputerName) { Write-PSFMessage -Level Verbose -Message "Working against: $server - listing services" -Target ($Services -Join ",") Write-PSFMessage -Level Verbose -Message "Working against: $server - listing Aos services" -Target ($searchServicesAos -Join ",") if ($null -ne $searchServicesAos -and $searchServicesAos.count -gt 0) { Write-PSFMessage -Level Verbose -Message "`$searchServicesAos used for searching" $colAosServices = Get-Service -ComputerName $server -Name $searchServicesAos -ErrorAction SilentlyContinue | Select-Object @{Name = "Server"; Expression = { $Server } }, Name, Status, DisplayName foreach ($service in $colAosServices) { if ((-not $ScanAllAosServices) -and ($service.DisplayName -NotLike $AosInstanceName)) { continue } $null = $res.Add($service) } } if ($null -ne $Services -and $Services.count -gt 0) { Write-PSFMessage -Level Verbose -Message "`$Services used for searching" $axServices = Get-Service -ComputerName $server -Name $Services -ErrorAction SilentlyContinue | Select-Object @{Name = "Server"; Expression = { $Server } }, Name, Status, DisplayName foreach ($service in $axServices) { if ($service.DisplayName -like "*AX Object Server*" ) { if ((-not $ScanAllAosServices) -and ($service.DisplayName -NotLike $AosInstanceName)) { continue } } $null = $res.Add($service) } } } $res.ToArray() | Select-Object Server, DisplayName, Status, Name } } <# .SYNOPSIS Get the SSRS configuration .DESCRIPTION Get all the SSRS configuration from the AX 2012 database .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER DatabaseName Name of the database Default value is: "MicrosoftDynamicsAx" .PARAMETER SqlUser User name of the SQL Server credential that you want to use when working against the database .PARAMETER SqlPwd Password of the SQL Server credential that you want to use when working against the database .EXAMPLE PS C:\> Get-AxSsrsConfig This will get all the stored SSRS configuration entries from the default DatabaseServer and the default Database. .EXAMPLE PS C:\> Get-AxAosInstance | Get-AxSsrsConfig This will get all AOS Instance from the local machine and pipe them to the Get-AxSsrsConfig cmdlet. The Get-AxSsrsConfig will the traverse every AOS Instance and their corresponding database for all SSRS configuration. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxSsrsConfig { [CmdletBinding()] param ( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 1)] [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 2)] [string] $DatabaseName = $Script:ActiveAosDatabase, [Parameter(Mandatory = $false, Position = 3)] [string] $SqlUser, [Parameter(Mandatory = $false, Position = 4)] [string] $SqlPwd ) begin { Invoke-TimeSignal -Start } process { $baseParams = Get-DeepClone $PSBoundParameters $baseParams.Add("TrustedConnection", $true) $UseTrustedConnection = Test-TrustedConnection $baseParams $SqlParams = @{ DatabaseServer = $DatabaseServer; DatabaseName = $DatabaseName; SqlUser = $SqlUser; SqlPwd = $SqlPwd } $sqlCommand = Get-SqlCommand @SqlParams -TrustedConnection $UseTrustedConnection $sqlCommand.CommandText = (Get-Content "$script:ModuleRoot\internal\sql\get-ssrsconfig.sql") -join [Environment]::NewLine try { Write-PSFMessage -Level InternalComment -Message "Executing a script against the database." -Target (Get-SqlString $sqlCommand) $sqlCommand.Connection.Open() $reader = $sqlCommand.ExecuteReader() while ($reader.Read() -eq $true) { [PSCustomObject][ordered]@{ ConfigurationId = "$($reader.GetString($($reader.GetOrdinal("CONFIGURATIONID"))))" Description = "$($reader.GetString($($reader.GetOrdinal("DESCRIPTION"))))" IsDefaultReportServer = [bool][int]"$($reader.GetInt32($($reader.GetOrdinal("ISDEFAULTREPORTLIBRARYSERVER"))))" SsrsServerName = "$($reader.GetString($($reader.GetOrdinal("SERVERID"))))" SsrsServerInstance = "$($reader.GetString($($reader.GetOrdinal("SERVERINSTANCE"))))" ReportManagerUrl = "$($reader.GetString($($reader.GetOrdinal("REPORTMANAGERURL"))))" ServerUrl = "$($reader.GetString($($reader.GetOrdinal("SERVERURL"))))" IsSharepointIntegrated = [bool][int]"$($reader.GetInt32($($reader.GetOrdinal("ISSHAREPOINTINTEGRATED"))))" ReportFolder = "$($reader.GetString($($reader.GetOrdinal("AXAPTAREPORTFOLDER"))))" IsDefaultReportmodelServer = [bool][int]"$($reader.GetInt32($($reader.GetOrdinal("ISDEFAULTREPORTMODELSERVER"))))" DataSourceName = "$($reader.GetString($($reader.GetOrdinal("DATASOURCENAME"))))" AosId = "$($reader.GetString($($reader.GetOrdinal("AOSID"))))" } } } catch { Write-PSFMessage -Level Host -Message "Something went wrong while working against the database" -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of errors" return } finally { if ($sqlCommand.Connection.State -ne [System.Data.ConnectionState]::Closed) { $sqlCommand.Connection.Close() } $sqlCommand.Dispose() } } end { Invoke-TimeSignal -End } } <# InstanceName : MicrosoftDynamicsAX ConfigurationName : DEBUG BinDirectory : C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\Bin\ ExecutablePath : C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\Bin\Ax32Serv.exe FileVersion : 6.3.6000.7046 ProductVersion : 6.3.6000.7046 FileVersionUpdated : 6.3.6000.7046 ProductVersionUpdated : 6.3.6000.7046 DatabaseServer : GJ-AX2012CU13 DatabaseName : MicrosoftDynamicsAX ModelstoreDatabase : MicrosoftDynamicsAX_model AosPort : 2712 WsdlPort : 8101 NetTcpPort : 8201 RegistryKeyPath : HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Dynamics Server\6.0\01 InstanceNumber : 01 ComputerName : GJ-AX2012CU13 SERVERID ISDEFAULTREPORTMODELSERVER SERVERURL ISDEFAULTREPORTLIBRARYSERVER AXAPTAREPORTFOLDER DESCRIPTION DATASOURCENAME REPORTMANAGERURL SERVERINSTANCE AOSID CONFIGURATIONID ISSHAREPOINTINTEGRATED RECVERSION RECID ESADM01 0 http://esadm01:81/ReportServer 1 DynamicsAX http://esadm01:81/Reports MSSQLSERVER 01@ESADM01 SSRS_LIVE 0 546259976 5637145176 #> <# .SYNOPSIS Get WMDP details .DESCRIPTION Get the most relevant WMDP details from your AX 2012 environment .EXAMPLE PS C:\> Get-AxWMDPDetails This will get all the relevant WMDP details from the AX 2012 environment .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxWMDPDetails { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] param ( ) Get-WMDPDetailsFromIIS } <# .SYNOPSIS Import an AX 2012 modelstore file .DESCRIPTION Import an AX 2012 modelstore file into the modelstore database .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER ModelstoreDatabase Name of the modelstore database Default value is: "MicrosoftDynamicsAx_model" Note: From AX 2012 R2 and upwards you need to provide the full name for the modelstore database. E.g. "AX2012R3_PROD_model" .PARAMETER SchemaName Name of the schema to import the modelstore into Default value is: "TempSchema" .PARAMETER Path Path to the location where you want the file to be exported Default value is: "c:\temp\ax2012.tools" .PARAMETER IdConflictMode Parameter to instruct how the import should handle ID conflicts if it hits any during the import Valid options: "Reject" "Push" "Overwrite" .PARAMETER Apply Instruct the cmdlet to switch modelstore with the SchemaName in as the current code .PARAMETER ShowOriginalProgress Instruct the cmdlet to show the standard output in the console Default is $false which will silence the standard output .PARAMETER OutputCommandOnly Instruct the cmdlet to only generate the needed command and not execute it .EXAMPLE PS C:\> Import-AXModelStoreV2 -SchemaName TempSchema -Path C:\Temp\ax2012.tools\MicrosoftDynamicsAx.axmodelstore This will execute the cmdlet with some of the default values. This will work against the SQL server that is on localhost. The database is expected to be "MicrosoftDynamicsAx_model". The import will import the modelstore into the "TempSchema". The path where the modelstore file you want to import must exists is: "c:\temp\ax2012.tools\MicrosoftDynamicsAx.axmodelstore". .NOTES Author: M�tz Jensen (@Splaxi) #> function Import-AXModelStoreV2 { [CmdletBinding(DefaultParameterSetName = "ImportModelstore")] [OutputType([System.String], ParameterSetName = "Generate")] Param( [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [string] $ModelstoreDatabase = $Script:ActiveAosModelstoredatabase, [Parameter(ParameterSetName = "ImportModelstore")] [Parameter(ParameterSetName = "ApplyModelstore")] [string] $SchemaName = "TempSchema", [Parameter(ParameterSetName = "ImportModelstore")] [string] $Path = (Join-Path $Script:DefaultTempPath "MicrosoftDynamicsAx.axmodelstore"), [ValidateSet("Reject", "Push", "Overwrite")] [Parameter(ParameterSetName = "ImportModelstore")] [string] $IdConflictMode, [Parameter(ParameterSetName = "ApplyModelstore")] [switch] $Apply, [switch] $ShowOriginalProgress, [Parameter(ParameterSetName = "Generate")] [switch] $OutputCommandOnly ) Invoke-TimeSignal -Start if (-not (Test-PathExists -Path $Path -Type Container -Create)) { return } if (-not (Test-PathExists -Path $Script:AxPowerShellModule -Type Leaf)) { return } $params = @{ Server = $DatabaseServer Database = $ModelstoreDatabase } if ($PSCmdlet.ParameterSetName -eq "ImportModelstore") { $params.File = $Path $params.SchemaName = $SchemaName if ($PSBoundParameters.ContainsKey("IdConflictMode")) { $params.IdConflict = $IdConflictMode } } elseif ($PSCmdlet.ParameterSetName -eq "ApplyModelstore") { $params.Apply = $SchemaName } if ($OutputCommandOnly) { $arguments = Convert-HashToArgString -InputObject $params "Import-AxModelStore $($arguments -join ' ')" } else { Write-PSFMessage -Level Verbose -Message "Starting the export of the model store" $null = Import-Module $Script:AxPowerShellModule $outputRes = Import-AXModelStore @params if ($ShowOriginalProgress) { $outputRes } Clear-Ax2012StandardPowershellModule } Invoke-TimeSignal -End } <# .SYNOPSIS Import AX 2012 model .DESCRIPTION Import AX 2012 model into the AX 2012 Model store .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER ModelstoreDatabase Name of the modelstore database Default value is: "MicrosoftDynamicsAx_model" Note: From AX 2012 R2 and upwards you need to provide the full name for the modelstore database. E.g. "AX2012R3_PROD_model" .PARAMETER Path Path to the folder containing the AX model file(s) that you want to import The cmdlet will traverse all sub folders for files and import them based on their names .PARAMETER ConflictMode Instructs the cmdlet to handle conflicts The list of options is: "Reject" "Push" "Overwrite" .PARAMETER Detailed Instruct the cmdlet to output detailed element names and AOT path while importing the model .PARAMETER CreateParents Instruct the cmdlet to create missing parents on import .PARAMETER NoOptimize Instruct the cmdlet to skip the optimization on import This makes sense if you are import more than 1-2 AX 2012 models at the same time .PARAMETER NoPrompt Instruct the cmdlet not to prompt you with anything .PARAMETER ShowOriginalProgress Instruct the cmdlet to show the standard output in the console Default is $false which will silence the standard output .PARAMETER OutputCommandOnly Instruct the cmdlet to output a script that you can execute manually later Using this will not import any AX 2012 models into the model store .EXAMPLE PS C:\> Import-AxModelV2 -Path "c:\temp\ax2012.tools\dev-models" The cmdlet will look for all the AX 2012 models located in "c:\temp\ax2012.tools\dev-models" or any of its sub folders. The ConflictMode is set to the default value of "OverWrite". The Database Server is set to the default value of "localhost". The Modelstore Database is set to the default value of "MicrosoftDynamicsAx_model". .EXAMPLE PS C:\> Import-AxModelV2 -Path "c:\temp\ax2012.tools\dev-models" -ShowOriginalProgress The cmdlet will look for all the AX 2012 models located in "c:\temp\ax2012.tools\dev-models" or any of its sub folders. The ConflictMode is set to the default value of "OverWrite". The Database Server is set to the default value of "localhost". The Modelstore Database is set to the default value of "MicrosoftDynamicsAx_model". It will show the original progress output for the import of the model file in real time. .NOTES Author: M�tz Jensen (@Splaxi) #> Function Import-AxModelV2 { [CmdletBinding()] [OutputType([System.String], ParameterSetName="Generate")] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [Parameter(ValueFromPipelineByPropertyName = $true)] [string] $ModelstoreDatabase = $Script:ActiveAosModelstoredatabase, [Parameter(Mandatory = $false)] [string] $Path = $Script:DefaultTempPath, [Parameter(Mandatory = $false)] [ValidateSet("Reject", "Push", "Overwrite")] [string] $ConflictMode = "Overwrite", [switch] $Detailed, [switch] $CreateParents, [switch] $NoOptimize, [switch] $NoPrompt, [switch] $ShowOriginalProgress, [Parameter(ParameterSetName = "Generate")] [switch] $OutputCommandOnly ) BEGIN { $null = Import-Module $Script:AxPowerShellModule if (-not (Test-PathExists -Path $Path -Type Container)) { return } } PROCESS { if (Test-PSFFunctionInterrupt) { return } Invoke-TimeSignal -Start $AxModelsPath = (Get-ChildItem -Path $Path | Where-Object { $_.PSIsContainer } | Sort-Object CreationTime -Descending | Select-Object -First 1 | Select-Object Fullname).FullName Write-PSFMessage -Level Verbose -Message "The newest / latest folder is: $AxModelsPath" -Target $AxModelsPath $AxModelFiles = Get-ChildItem -Path $AxModelsPath -Recurse -File $params = @{ Server = $DatabaseServer; Conflict = $ConflictMode; Database = $ModelstoreDatabase } $paramsSwitch = Get-HashtableKey -InputObject $PSBoundParameters -Keys @("CreateParents", "NoOptimize", "NoPrompt") if($Detailed){ $paramsSwitch.Add("Details", $Detailed) } foreach ($item in $AxModelFiles) { Write-PSFMessage -Level Verbose -Message "Working on file: $($item.FullName)" -Target $item.FullName $clonedParams = Get-DeepClone $params $clonedParams.File = $item.FullName if ($OutputCommandOnly) { $arguments = Convert-HashToArgString -InputObject $clonedParams $argumentsSwitch = Convert-HashToArgStringSwitch -InputObject $paramsSwitch "Install-AxModel $($arguments -join ' ') $($argumentsSwitch -join ' ')" } else { $outputRes = Install-AXModel @clonedParams @paramsSwitch if($ShowOriginalProgress) { $outputRes } } } Invoke-TimeSignal -End } END { Clear-Ax2012StandardPowershellModule } } <# .SYNOPSIS Initialize an AX 2012 modelstore .DESCRIPTION Initialize an AX 2012 modelstore against a modelstore database .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER ModelstoreDatabase Name of the modelstore database Default value is: "MicrosoftDynamicsAx_model" Note: From AX 2012 R2 and upwards you need to provide the full name for the modelstore database. E.g. "AX2012R3_PROD_model" .PARAMETER SchemaName Name of the schema in the modelstore database that you want to work against Default value is: "TempSchema" .PARAMETER DropSchema Instruct the cmdlet to drop the schema supplied with the -SchemaName parameter .PARAMETER CreateSchema Instruct the cmdlet to create the schema supplied with the -SchemaName parameter .PARAMETER CreateDb Instruct the cmdlet to create a new modelstore inside the supplied -ModelstoreDatabase parameter .PARAMETER ShowOriginalProgress Instruct the cmdlet to show the standard output in the console Default is $false which will silence the standard output .PARAMETER OutputCommandOnly Instruct the cmdlet to only generate the needed command and not execute it .EXAMPLE PS C:\> Initialize-AXModelStoreV2 -SchemaName TempSchema -CreateSchema This will execute the cmdlet with some of the default values. This will work against the SQL server that is on localhost. The database is expected to be "MicrosoftDynamicsAx_model". The cmdlet will create the "TempSchema" schema inside the modelstore database. .EXAMPLE PS C:\> Initialize-AXModelStoreV2 -SchemaName TempSchema -DropSchema This will execute the cmdlet with some of the default values. This will work against the SQL server that is on localhost. The database is expected to be "MicrosoftDynamicsAx_model". The cmdlet will drop the "TempSchema" schema inside the modelstore database. .NOTES Author: M�tz Jensen (@Splaxi) #> function Initialize-AXModelStoreV2 { [CmdletBinding(DefaultParameterSetName = "CreateSchema")] [OutputType([System.String], ParameterSetName = "Generate")] param ( [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [string] $ModelstoreDatabase = $Script:ActiveAosModelstoredatabase, [Parameter(ParameterSetName = "Drop")] [Parameter(ParameterSetName = "CreateSchema")] [string] $SchemaName = "TempSchema", [Parameter(ParameterSetName = "Drop")] [switch] $DropSchema, [Parameter(ParameterSetName = "CreateSchema")] [switch] $CreateSchema, [Parameter(ParameterSetName = "CreateDB")] [switch] $CreateDb, [switch] $ShowOriginalProgress, [Parameter(ParameterSetName = "Generate")] [switch] $OutputCommandOnly ) Invoke-TimeSignal -Start if (-not (Test-PathExists -Path $Script:AxPowerShellModule -Type Leaf)) { return } $params = @{ Server = $DatabaseServer Database = $ModelstoreDatabase } if ($PSCmdlet.ParameterSetName -eq "CreateSchema") { $params.SchemaName = $SchemaName } elseif ($PSCmdlet.ParameterSetName -eq "Drop") { $params.Drop = $SchemaName } elseif ($PSCmdlet.ParameterSetName -eq "CreateDB") { $params.CreateDB = $true } if ($OutputCommandOnly) { $arguments = Convert-HashToArgString -InputObject $params "Initialize-AXModelStore $($arguments -join ' ')" } else { Write-PSFMessage -Level Verbose -Message "Starting the initialization of the model store" $null = Import-Module $Script:AxPowerShellModule $outputRes = Initialize-AXModelStore @params if ($ShowOriginalProgress) { $outputRes } Clear-Ax2012StandardPowershellModule } Invoke-TimeSignal -End } <# .SYNOPSIS Start the AxBuild.exe .DESCRIPTION Invoke the AxBuild.exe with the necessary parameters to make it compile your application .PARAMETER BinDirectory The full path to the bin directory where the AOS instance is physical installed .PARAMETER AlternativeBinPath The full path to the client bin directory where AX 2012 Client is physical installed .PARAMETER InstanceNumber The 2 digit ([0-9][0-9]) number that the AOS instance has on the server .PARAMETER DatabaseServer The name of the server running SQL Server .PARAMETER ModelstoreDatabase The name of the AX 2012 modelstore database .PARAMETER Workers Number of workers that you want to utilize while compiling The built-in logic from AxBuild.exe will choose a number equal to your visible cores Leaving it blank or with 0 (Zero) will use the built-in logic from AxBuild.exe .PARAMETER OutputPath Path to the log file you want AxBuild.exe to output to Default location is: "c:\temp\ax2012.tools\AxBuild\" .PARAMETER ShowOriginalProgress Instruct the cmdlet to show the standard output in the console Default is $false which will silence the standard output .PARAMETER OutputCommandOnly Instruct the cmdlet to output a script that you can execute manually later Using this will not import any AX 2012 models into the model store .EXAMPLE PS C:\> Get-AxAosInstance | Invoke-AxBuild This will find all AOS instances using the Get-AxAosInstance on the machine and pipe them to Invoke-AxBuild. For each AOS instance found it will start the AxBuild.exe against their individual details. It will store the log file under the default ax2012.tools folder. .EXAMPLE PS C:\> Invoke-AxBuild This will start the AxBuild.exe against the ActiveAos configuration. It will store the log file under the default ax2012.tools folder. .NOTES Author: M�tz Jensen (@Splaxi) #> function Invoke-AxBuild { [CmdletBinding()] [OutputType([System.String], ParameterSetName = "Generate")] param ( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('Path')] [string] $BinDirectory = $Script:ActiveAosBindirectory, [Alias('AltBin')] [string] $AlternativeBinPath = $Script:ClientBin, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('Aos')] [string] $InstanceNumber = $Script:ActiveAosInstanceNumber, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('DBServer')] [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('Modelstore')] [string] $ModelstoreDatabase = $Script:ActiveAosModelstoredatabase, [int] $Workers, [string] $OutputPath = $(Join-Path $Script:DefaultTempPath "AxBuildLog"), [switch] $ShowOriginalProgress, [Parameter(ParameterSetName = "Generate")] [switch] $OutputCommandOnly ) begin { $executable = Join-Path $BinDirectory "AXBuild.exe" $compiler = Join-Path $BinDirectory "ax32serv.exe" if (-not (Test-PathExists -Path $OutputPath -Type Container -Create)) { return } if (-not (Test-PathExists -Path $executable, $compiler -Type Leaf)) { return } } process { if (Test-PSFFunctionInterrupt) { return } Invoke-TimeSignal -Start $params = New-Object System.Collections.Generic.List[string] $params.Add("xppcompileall") $params.Add("/altbin=`"$AlternativeBinPath`"") $params.Add("/aos=$InstanceNumber") $params.Add("/dbserver=`"$DatabaseServer`"") $params.Add("/modelstore=`"$ModelstoreDatabase`"") $params.Add("/log=`"$OutputPath`"") $params.Add("/compiler=`"$compiler`"") if ((-not ($null -eq $Workers)) -and ($Workers -gt 0)) { $params.Add("/workers=$Workers") } Write-PSFMessage -Level Verbose -Message "Starting $executable with $($params -join " ")" -Target ($params -join " ") Invoke-Process -Executable $executable -Params $($params.ToArray()) -ShowOriginalProgress:$ShowOriginalProgress -OutputCommandOnly:$OutputCommandOnly Invoke-TimeSignal -End } } <# .SYNOPSIS Invoke a http request for a Logic App .DESCRIPTION Invoke a Logic App using a http request and pass a json object with details about the calling function .PARAMETER Url The URL for the http endpoint that you want to invoke .PARAMETER Email The email address of the receiver of the message that the cmdlet will send .PARAMETER Subject Subject string to apply to the email and to the IM message .PARAMETER IncludeAll Instruct the cmdlet to include all cmdlets (names only) from the pipeline .PARAMETER AsJob Instruct the cmdlet to run the invocation as a job (async) .EXAMPLE PS C:\> Invoke-AxLogicApp -Email administrator@contoso.com -Subject "Work is done" -Url https://prod-35.westeurope.logic.azure.com:443/ This will simply invoke an Azure Logic App with the email, subject and URL parameters that are needed to invoke an Azure Logic App. A notification will be sent to "administrator@contoso.com". .EXAMPLE PS C:\> Start-AxEnvironment -Aos | Invoke-AxLogicApp This will execute the sync process and when it is done it will invoke an Azure Logic App with the default parameters that have been configured for the system. .EXAMPLE PS C:\> Start-AxEnvironment -Aos | Invoke-AxLogicApp -Email administrator@contoso.com -Subject "Work is done" -Url https://prod-35.westeurope.logic.azure.com:443/ This will execute the sync process and when it is done it will invoke an Azure Logic App with the email, subject and URL parameters that are needed to invoke an Azure Logic App. A notification will be sent to "administrator@contoso.com". .NOTES Author: M�tz Jensen (@Splaxi) #> function Invoke-AxLogicApp { param ( [string] $Url = $Script:ActiveLogicappUrl, [string] $Email = $Script:ActiveLogicappEmail, [string] $Subject = $Script:ActiveLogicappSubject, [switch] $IncludeAll, [switch] $AsJob ) begin { } process { $pipes = $MyInvocation.Line.Split("|") $arrList = New-Object -TypeName "System.Collections.ArrayList" foreach ($item in $pipes.Trim()) { $null = $arrList.Add( $item.Split(" ")[0]) } $strMessage = ""; if ($IncludeAll) { $strMessage = $arrList -Join ", " } else { $strMessage = $arrList[$MyInvocation.PipelinePosition - 2] } $strMessage = "The following list of cmdlets has executed: $strMessage" Invoke-PSNMessage -Url $URL -ReceiverEmail $Email -Subject $Subject -Message $strMessage -AsJob:$AsJob } end { } } <# .SYNOPSIS Remove environment configuration .DESCRIPTION Remove a environment configuration from the configuration store .PARAMETER Name Name of the environment configuration you want to remove from the configuration store .PARAMETER Temporary Instruct the cmdlet to only temporarily remove the environment configuration from the configuration store .EXAMPLE PS C:\> Remove-AxEnvironmentConfig -Name "UAT" This will remove the environment configuration name "UAT" from the machine. .NOTES Tags: Servicing, Environment, Config, Configuration, Servers Author: M�tz Jensen (@Splaxi) .LINK Add-AxEnvironmentConfig .LINK Clear-AxActiveEnvironmentConfig .LINK Get-AxActiveEnvironmentConfig .LINK Get-AxEnvironmentConfig .LINK Set-AxActiveEnvironmentConfig #> function Remove-AxEnvironmentConfig { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding()] [OutputType()] param ( [Parameter(Mandatory = $true, Position = 1)] [string] $Name, [switch] $Temporary ) $Name = $Name.ToLower() if ($Name -match '\*') { Write-PSFMessage -Level Host -Message "The name cannot contain <c='em'>wildcard character</c>." Stop-PSFFunction -Message "Stopping because the name contains wildcard character." return } if (-not ((Get-PSFConfig -FullName "ax2012.tools.environment.*.name").Value -contains $Name)) { Write-PSFMessage -Level Host -Message "A environment configuration with that name <c='em'>doesn't exists</c>." Stop-PSFFunction -Message "Stopping because a environment message configuration with that name doesn't exists." return } $res = (Get-PSFConfig -FullName "ax2012.tools.environment.config.name").Value if ($res -eq $Name) { Write-PSFMessage -Level Host -Message "The active environment configuration is the <c='em'>same as the one you're trying to remove</c>. Please set another configuration as active, before removing this one. You could also call Clear-AxActiveEnvironmentConfig." Stop-PSFFunction -Message "Stopping because the active environment configuration is the same as the one trying to be removed." return } foreach ($config in Get-PSFConfig -FullName "ax2012.tools.environment.$Name.*") { Set-PSFConfig -FullName $config.FullName -Value "" if (-not $Temporary) { Unregister-PSFConfig -FullName $config.FullName -Scope UserDefault } } } <# .SYNOPSIS Fix table and field ID conflicts .DESCRIPTION Fixes both table and field IDs in the AX SqlDictionary (data db) to match the AX code (Model db) Useful for after a database has been restored and the table or field IDs do not match Run this command instead of letting the database synchronization process drop and recreate the table Before running: Stop the AOS Always take the appropriate SQL backups before running this script After running: Start the AOS Sync the database within AX Note: Objects that are new in AOT will get created in SQL dictionary when synchronization happens .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER DatabaseName Name of the database Default value is: "MicrosoftDynamicsAx" .PARAMETER ModelstoreDatabase Name of the modelstore database Default value is: "MicrosoftDynamicsAx_model" .PARAMETER SqlUser User name of the SQL Server credential that you want to use when working against the database .PARAMETER SqlPwd Password of the SQL Server credential that you want to use when working against the database .PARAMETER Force Instruct the cmdlet to overwrite any existing bak (backup) tables from previous executions .PARAMETER OutputCommandOnly When provided the SQL is returned and not executed Note: This is useful for troubleshooting or providing the script to a DBA with access to the server .EXAMPLE PS C:\> Resolve-AxTableFieldIDs This will execute the cmdlet with all the default values. This will work against the SQL server that is on localhost. The database is expected to be "MicrosoftDynamicsAx_model". .NOTES Author: Dag Calafell, III (@dodiggitydag) Reference: http://calafell.me/the-ultimate-ax-2012-table-and-field-id-fix-for-synchronization-errors/ #> Function Resolve-AxTableFieldIDs { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding()] [OutputType([System.String], ParameterSetName = "Generate")] Param( [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [string] $DatabaseName = $Script:ActiveAosDatabase, [string] $ModelstoreDatabase = $Script:ActiveAosModelstoredatabase, [string] $SqlUser, [string] $SqlPwd, [Switch] $Force, [Parameter(ParameterSetName = "Generate")] [Switch] $OutputCommandOnly ) Invoke-TimeSignal -Start $baseParams = Get-DeepClone $PSBoundParameters $baseParams.Add("TrustedConnection", $true) $UseTrustedConnection = Test-TrustedConnection $baseParams $SqlParams = @{ DatabaseServer = $DatabaseServer; DatabaseName = $DatabaseName; SqlUser = $SqlUser; SqlPwd = $SqlPwd } $forceParameterValue = "0" if ($Force) { $forceParameterValue = "1" } $sqlCommand = Get-SqlCommand @SqlParams -TrustedConnection $UseTrustedConnection $commandText = (Get-Content "$script:ModuleRoot\internal\sql\resolve-sqldictionaryids.sql") -join [Environment]::NewLine $sqlCommand.CommandText = $commandText.Replace('@DatabaseName', $DatabaseName).Replace('@ModelDatabaseName', $ModelstoreDatabase).Replace("@ForceValue", $forceParameterValue) if ($OutputCommandOnly) { (Get-SqlString $sqlCommand) } else { $handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { param($sender, $event) Write-PSFMessage -Level Host -Message $($event.Message) -Target $($event.Message) } $sqlCommand.Connection.add_InfoMessage($handler) $sqlCommand.Connection.FireInfoMessageEventOnUserErrors = $true; try { Write-PSFMessage -Level InternalComment -Message "Executing a script against the database." -Target (Get-SqlString $sqlCommand) $sqlCommand.Connection.Open() $sqlCommand.ExecuteNonQuery() Write-PSFMessage -Level Verbose -Message "SQL query executed." } catch { Write-PSFMessage -Level Host -Message "Something went wrong while working against the database" -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of errors" return } finally { if ($sqlCommand.Connection.State -ne [System.Data.ConnectionState]::Closed) { $sqlCommand.Connection.Close() } $sqlCommand.Dispose() } } Invoke-TimeSignal -End } <# .SYNOPSIS Set the active AX 2012 AOS configuration .DESCRIPTION Set the active AX 2012 AOS details and store it into the configuration storage .PARAMETER ComputerName The name of the computer / server that AOS resides on .PARAMETER BinDirectory The full path to the bin directory where the AOS instance is physical installed .PARAMETER InstanceNumber The 2 digit ([0-9][0-9]) number that the AOS instance has on the server .PARAMETER InstanceName The instance name the AOS server is registered with .PARAMETER DatabaseServer The name of the server running SQL Server .PARAMETER DatabaseName The name of the AX 2012 business data database .PARAMETER ModelstoreDatabase The name of the AX 2012 modelstore database .PARAMETER AosPort The TCP port that the AX 2012 AOS server is communicating with the AX clients on .PARAMETER WsdlPort The TCP port that the AX 2012 AOS server is communicating with all WSDL consuming applications on .PARAMETER NetTcpPort The TCP port that the AX 2012 AOS server is communicating with all NetTcp consuming applications on .PARAMETER ConfigStorageLocation Parameter used to instruct where to store the configuration objects The default value is "User" and this will store all configuration for the active user Valid options are: "User" "System" "System" will store the configuration so all users can access the configuration objects .PARAMETER Temporary Instruct the cmdlet to only temporarily override the persisted settings in the configuration storage .PARAMETER Clear Instruct the cmdlet to clear out all the stored configuration values .EXAMPLE PS C:\> Get-AxAosInstance | Select-Object -First 1 | Set-AxActiveAosConfig This will get all the AX 2012 AOS instances from the local machine and only select the first output. The output from the first AOS instance is saved into the configuration store. .EXAMPLE PS C:\> Set-AxActiveAosConfig -ComputerName AX2012PROD -DatabaseServer SQLSERVER -DatabaseName AX2012R3_PROD -ModelstoreDatabase AX2012R3_PROD_model -AosPort 2712 This will update the active AOS configuration store settings. The computer name will be registered to: AX2012PROD The database server will be registered to: SQLSERVER The database name will be registered to: AX2012R3_PROD The model store database will be registered to: AX2012R3_PROD_model The AOS port will be registered to: 2712 .EXAMPLE PS C:\> Set-AxActiveAosConfig -Clear This will clear out all the stored configuration values. It updates all the internal configuration variables, so all aos default values across the module will be empty. .NOTES Author: M�tz Jensen (@Splaxi) #> function Set-AxActiveAosConfig { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName = $true, Position = 1)] [string[]] $ComputerName = @($env:computername), [Parameter(ValueFromPipelineByPropertyName = $true, Position = 2)] [string] $BinDirectory, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 3)] [string] $InstanceNumber, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 4)] [string] $InstanceName, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 5)] [string] $DatabaseServer, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 6)] [string] $DatabaseName, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 7)] [string] $ModelstoreDatabase, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 8)] [string] $AosPort, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 9)] [string] $WsdlPort, [Parameter(ValueFromPipelineByPropertyName = $true, Position = 10)] [string] $NetTcpPort, [ValidateSet('User', 'System')] [string] $ConfigStorageLocation = "User", [switch] $Temporary, [switch] $Clear ) $configScope = Test-ConfigStorageLocation -ConfigStorageLocation $ConfigStorageLocation if (Test-PSFFunctionInterrupt) { return } if ($Clear) { Write-PSFMessage -Level Verbose -Message "Clearing all the ax2012.tools.active.aos configurations." foreach ($item in (Get-PSFConfig -FullName ax2012.tools.active.aos*)) { Set-PSFConfig -Fullname $item.FullName -Value "" if (-not $Temporary) { Register-PSFConfig -FullName $item.FullName -Scope $configScope } } } else { foreach ($key in $PSBoundParameters.Keys) { $value = $PSBoundParameters.Item($key) Write-PSFMessage -Level Verbose -Message "Working on $key with $value" -Target $value switch ($key) { "ComputerName" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.computername' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.computername' -Scope $configScope } } "BinDirectory" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.bindirectory' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.bindirectory' -Scope $configScope } } "InstanceNumber" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.instance.number' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.instance.number' -Scope $configScope } } "InstanceName" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.instancename' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.instancename' -Scope $configScope } } "DatabaseServer" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.databaseserver' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.databaseserver' -Scope $configScope } } "DatabaseName" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.database' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.database' -Scope $configScope } } "ModelstoreDatabase" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.modelstoredatabase' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.modelstoredatabase' -Scope $configScope } } "AosPort" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.aos.port' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.aos.port' -Scope $configScope } } "WsdlPort" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.wsdl.port' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.wsdl.port' -Scope $configScope } } "NetTcpPort" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.nettcp.port' -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.nettcp.port' -Scope $configScope } } Default {} } } } Update-ActiveVariables } <# .SYNOPSIS Set the active environment configuration .DESCRIPTION Updates the current active environment configuration with a new one Use this to update the default parameters across the module, to make it easier to call your different commands .PARAMETER Name Name of the environment configuration you want to load into the active environment configuration .PARAMETER Temporary Instruct the cmdlet to only temporarily override the persisted settings in the configuration store .EXAMPLE PS C:\> Set-AxActiveEnvironmentConfig -Name "UAT" This will set the environment configuration named "UAT" as the active configuration. .NOTES Tags: Servicing, Environment, Config, Configuration, Servers Author: M�tz Jensen (@Splaxi) .LINK Add-AxEnvironmentConfig .LINK Get-AxActiveEnvironmentConfig .LINK Get-AxEnvironmentConfig #> function Set-AxActiveEnvironmentConfig { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding()] [OutputType()] param ( [Parameter(Mandatory = $true, Position = 1)] [string] $Name, [switch] $Temporary ) if($Name -match '\*') { $messageString = "The name cannot contain <c='em'>wildcard character</c>." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because the name contains wildcard character." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>',''))) return } if (-not ((Get-PSFConfig -FullName "ax2012.tools.environment.*.name").Value -contains $Name)) { $messageString = "An environment configuration with that name <c='em'>doesn't exists</c>." Write-PSFMessage -Level Host -Message $messageString Stop-PSFFunction -Message "Stopping because an OData message configuration with that name doesn't exists." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>',''))) return } Set-PSFConfig -FullName "ax2012.tools.active.environment.config.name" -Value $Name if (-not $Temporary) { Register-PSFConfig -FullName "ax2012.tools.active.environment.config.name" -Scope UserDefault } Update-ActiveEnvironmentVariables } <# .SYNOPSIS Set the details for the logic app invoke cmdlet .DESCRIPTION Store the needed details for the module to execute an Azure Logic App using a HTTP request .PARAMETER Url The URL for the http request endpoint of the desired logic app .PARAMETER Email The receiving email address that should be notified .PARAMETER Subject The subject of the email that you want to send .PARAMETER ConfigStorageLocation Parameter used to instruct where to store the configuration objects The default value is "User" and this will store all configuration for the active user Valid options are: "User" "System" "System" will store the configuration so all users can access the configuration objects .PARAMETER Temporary Instruct the cmdlet to only temporarily override the persisted settings in the configuration storage .EXAMPLE PS C:\> Set-AxLogicAppConfig -Email administrator@contoso.com -Subject "Work is done" -Url https://prod-35.westeurope.logic.azure.com:443/ This will set all the details about invoking the Logic App. .EXAMPLE PS C:\> Set-AxLogicAppConfig -Email administrator@contoso.com -Subject "Work is done" -Url https://prod-35.westeurope.logic.azure.com:443/ -ConfigStorageLocation "System" This will set all the details about invoking the Logic App. The data will be stored in the system wide configuration storage, which makes it accessible from all users. .EXAMPLE PS C:\> Set-AxLogicAppConfig -Email administrator@contoso.com -Subject "Work is done" -Url https://prod-35.westeurope.logic.azure.com:443/ -Temporary This will set all the details about invoking the Logic App. The update will only last for the rest of this PowerShell console session. .NOTES Author: M�tz Jensen (@Splaxi) #> function Set-AxLogicAppConfig { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding()] param ( [Parameter(Mandatory = $true )] [string] $Url, [Parameter(Mandatory = $true )] [string] $Email, [Parameter(Mandatory = $true )] [string] $Subject, [ValidateSet('User', 'System')] [string] $ConfigStorageLocation = "User", [switch] $Temporary ) $configScope = Test-ConfigStorageLocation -ConfigStorageLocation $ConfigStorageLocation if (Test-PSFFunctionInterrupt) { return } foreach ($key in $PSBoundParameters.Keys) { $value = $PSBoundParameters.Item($key) $name = $null Write-PSFMessage -Level Verbose -Message "Working on $key with $value" -Target $value switch ($key) { "Email" { $name = 'active.logicapp.email' $Script:LogicAppEmail = $value } "Url" { $name = 'active.logicapp.url' $Script:LogicAppUrl = $value } "Subject" { $name = 'active.logicapp.subject' $Script:LogicAppSubject = $value } Default {} } Set-PSFConfig -Module 'ax2012.tools' -Name $name -Value $value if (-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name $name -Scope $configScope } } } <# .SYNOPSIS Set the parameter sniffing configuration .DESCRIPTION Set the parameter sniffing value in the database based on the released hotfix from Microsoft for AX 2012 .PARAMETER DatabaseServer Server name of the database server Default value is: "localhost" .PARAMETER DatabaseName Name of the database Default value is: "MicrosoftDynamicsAx" .PARAMETER SqlUser User name of the SQL Server credential that you want to use when working against the database .PARAMETER SqlPwd Password of the SQL Server credential that you want to use when working against the database .PARAMETER OutputCommandOnly When provided the SQL is returned and not executed Note: This is useful for troubleshooting or providing the script to a DBA with access to the server .EXAMPLE PS C:\> Set-AxParameterSniffingSetting This will configure the correct parameter sniffing settings. .NOTES Author: M�tz Jensen (@Splaxi) .LINK https://community.dynamics.com/365/financeandoperations/b/axsupport/posts/how-to-proactively-avoid-parameter-sniffing-step-by-step #> function Set-AxParameterSniffingSetting { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [CmdletBinding()] #[OutputType()] param ( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 1)] [string] $DatabaseServer = $Script:ActiveAosDatabaseserver, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 2)] [string] $DatabaseName = $Script:ActiveAosDatabase, [Parameter(Mandatory = $false, Position = 3)] [string] $SqlUser, [Parameter(Mandatory = $false, Position = 4)] [string] $SqlPwd, [Switch] $OutputCommandOnly ) Invoke-TimeSignal -Start $baseParams = Get-DeepClone $PSBoundParameters $baseParams.Add("TrustedConnection", $true) $UseTrustedConnection = Test-TrustedConnection $baseParams $SqlParams = @{ DatabaseServer = $DatabaseServer; DatabaseName = $DatabaseName; SqlUser = $SqlUser; SqlPwd = $SqlPwd } $sqlCommand = Get-SqlCommand @SqlParams -TrustedConnection $UseTrustedConnection $sqlCommand.CommandText = (Get-Content "$script:ModuleRoot\internal\sql\set-axparametersniffingsetting.sql") -join [Environment]::NewLine if ($OutputCommandOnly) { (Get-SqlString $sqlCommand) } else { try { Write-PSFMessage -Level InternalComment -Message "Executing a script against the database." -Target (Get-SqlString $sqlCommand) $sqlCommand.Connection.Open() $sqlCommand.ExecuteNonQuery() Write-PSFMessage -Level Verbose -Message "SQL query executed." } catch { Write-PSFMessage -Level Host -Message "Something went wrong while working against the database" -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of errors" return } finally { if ($sqlCommand.Connection.State -ne [System.Data.ConnectionState]::Closed) { $sqlCommand.Connection.Close() } $sqlCommand.Dispose() } } Invoke-TimeSignal -End } <# .SYNOPSIS Start AX 2012 service(s) .DESCRIPTION Start AX 2012 service(s) on the computer .PARAMETER Server Name of the computer(s) that you want to work against Default value is the name from the ComputerName from AxActiveAosConfiguration .PARAMETER DisplayName DisplayName of windows service that you want to work against Accepts wildcards for searching. E.g. -DisplayName "*ax*obj*" .PARAMETER Name Name of the Windows Service that you want to work against This parameter is used when piping in the details Designed to work together with the Get-AxEnvironment cmdlet .PARAMETER ShowOriginalProgress Instruct the cmdlet to output the status for the service .EXAMPLE PS C:\> Start-AxService -Server TEST-AOS-01 -DisplayName *ax*obj* This will start the service(s) that match the search pattern "*ax*obj*" on the server named "TEST-AOS-01". .EXAMPLE PS C:\> Start-AxService -Server TEST-AOS-01 -DisplayName *ax*obj* -ShowOriginalProgress This will start the service(s) that match the search pattern "*ax*obj*" on the server named "TEST-AOS-01". It will show the progress of starting the service(s) in the console. It will show the status for the service(s) on the server afterwards. .EXAMPLE PS C:\> Get-AxService -ComputerName TEST-AOS-01 -Aos | Start-AxService -ShowOriginalProgress This will scan the "TEST-AOS-01" server for all AOS instances (services) and start them. It will show the progress of starting the service(s) in the console. It will show the status for the service(s) on the server afterwards. .NOTES Author: M�tz Jensen (@Splaxi) #> function Start-AxService { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = "Pipeline")] [Parameter(ParameterSetName = "Default", Position = 1)] [Alias('ComputerName')] [string[]] $Server = $Script:ActiveAosComputername, [Parameter(Mandatory = $true, ParameterSetName = "Default", Position = 2)] [string] $DisplayName, [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = "Pipeline")] [string[]] $Name, [switch] $ShowOriginalProgress ) begin { $output = New-Object System.Collections.ArrayList $warningActionValue = "SilentlyContinue" if ($ShowOriginalProgress) { $warningActionValue = "Continue" } } process { $baseParams = @{ComputerName = $Server; ErrorAction = "SilentlyContinue" } if ($PSBoundParameters.ContainsKey("Name")) { foreach ($item in $Name) { if (($item.Trim().Length -eq 0) -or ($Name -eq "*")) { Write-PSFMessage -Level Host -Message "It seems that you didn't provide any Name. That would result in starting all services." -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of missing filters." return } } if (Test-PSFFunctionInterrupt) { return } $baseParams.Name = $Name } else { if (($DisplayName.Length -gt 0) -and (-not($DisplayName -eq "*"))) { if ($DisplayName -notmatch "\*" ) { $DisplayName = "*$DisplayName*" } $baseParams.DisplayName = $DisplayName } else { Write-PSFMessage -Level Host -Message "It seems that you didn't provide any Display Name. That would result in starting all services." -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of missing filters." return } } Invoke-TimeSignal -Start Get-Service @baseParams | Start-Service -ErrorAction SilentlyContinue -WarningAction $warningActionValue $service = Get-Service @baseParams | Select-Object @{Name = "Server"; Expression = { $Server } }, Name, Status, DisplayName $null = $output.Add($service) Invoke-TimeSignal -End } end { $output.ToArray() | Select-Object Server, DisplayName, Status, Name } } <# .SYNOPSIS Stop AX 2012 service(s) .DESCRIPTION Stop AX 2012 service(s) on the computer .PARAMETER Server Name of the computer(s) that you want to work against Default value is the name from the ComputerName from AxActiveAosConfiguration .PARAMETER DisplayName DisplayName of windows service that you want to work against Accepts wildcards for searching. E.g. -DisplayName "*ax*obj*" .PARAMETER Name Name of the Windows Service that you want to work against This parameter is used when piping in the details Designed to work together with the Get-AxEnvironment cmdlet .PARAMETER ShowOriginalProgress Instruct the cmdlet to output the status for the service .PARAMETER Force Instruct the cmdlet to force the stopping of the service .EXAMPLE PS C:\> Stop-AxService -Server TEST-AOS-01 -DisplayName *ax*obj* This will stop the service(s) that match the search pattern "*ax*obj*" on the server named "TEST-AOS-01". .EXAMPLE PS C:\> Stop-AxService -Server TEST-AOS-01 -DisplayName *ax*obj* -ShowOriginalProgress This will stop the service(s) that match the search pattern "*ax*obj*" on the server named "TEST-AOS-01". It will show the progress of stopping the service(s) in the console. It will show the status for the service(s) on the server afterwards. .EXAMPLE PS C:\> Get-AxEnvironment -ComputerName TEST-AOS-01 -Aos | Stop-AxEnvironment -ShowOriginalProgress This will scan the "TEST-AOS-01" server for all AOS instances (services) and stop them. It will show the progress of stopping the service(s) in the console. It will show the status for the service(s) on the server afterwards. .NOTES Author: M�tz Jensen (@Splaxi) #> function Stop-AxService { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = "Pipeline")] [Parameter(ParameterSetName = "Default", Position = 1)] [Alias('ComputerName')] [string[]] $Server = $Script:ActiveAosComputername, [Parameter(Mandatory = $true, ParameterSetName = "Default", Position = 2)] [string] $DisplayName, [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = "Pipeline")] [string[]] $Name, [switch] $ShowOriginalProgress, [switch] $Force ) begin { $output = New-Object System.Collections.ArrayList $warningActionValue = "SilentlyContinue" if ($ShowOriginalProgress) { $warningActionValue = "Continue" } } process { $baseParams = @{ComputerName = $Server; ErrorAction = "SilentlyContinue" } if ($PSBoundParameters.ContainsKey("Name")) { foreach ($item in $Name) { if (($item.Trim().Length -eq 0) -or ($Name -eq "*")) { Write-PSFMessage -Level Host -Message "It seems that you didn't provide any Name. That would result in shutting down all services." -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of missing filters." return } } if (Test-PSFFunctionInterrupt) { return } $baseParams.Name = $Name } else { if (($DisplayName.Length -gt 0) -and (-not($DisplayName -eq "*"))) { if ($DisplayName -notmatch "\*" ) { $DisplayName = "*$DisplayName*" } $baseParams.DisplayName = $DisplayName } else { Write-PSFMessage -Level Host -Message "It seems that you didn't provide any Display Name. That would result in shutting down all services." -Exception $PSItem.Exception Stop-PSFFunction -Message "Stopping because of missing filters." return } } Invoke-TimeSignal -Start Write-PSFMessage -Level Verbose -Message "Stopping the specified services." -Target ((Convert-HashToArgString $baseParams) -join ",") Get-Service @baseParams | Stop-Service -Force:$Force -ErrorAction SilentlyContinue -WarningAction $warningActionValue $service = Get-Service @baseParams | Select-Object @{Name = "Server"; Expression = { $Server } }, Name, Status, DisplayName $null = $output.Add($service) Invoke-TimeSignal -End } end { $output.ToArray() | Select-Object Server, DisplayName, Status, Name } } |