commands.ps1
<# .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)) { $RegOuter = Get-ItemProperty -Path $($RegKey.Name.Replace("HKEY_CURRENT_USER", "HKCU:")) $RegInner = Get-ItemProperty -Path (Join-Path $RegKey.Name $RegOuter.Current).Replace("HKEY_CURRENT_USER", "HKCU:") $RegInner.bindir } } <# .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, Position = 1)] [HashTable] $InputObject, [Parameter(Mandatory = $true, Position = 2)] [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 All Switch to instruct the cmdlet to return all services .PARAMETER Aos Switch to instruct the cmdlet to return AOS .PARAMETER ManagementReporter Switch to instruct the cmdlet to return ManagementReporter .PARAMETER DIXF Switch to 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] $All = [switch]::Present, [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") { $All = $false } Write-PSFMessage -Level Verbose -Message "The PSBoundParameters was" -Target $PSBoundParameters $MRService = "MR2012ProcessService" $dixfname = "Microsoft.Dynamics.AX.Framework.Tools.DMF.SSISHelperService.exe" [System.Collections.ArrayList]$Services = New-Object -TypeName "System.Collections.ArrayList" if ($All) { for ($i = 1; $i -lt 100; $i++) { $null = $Services.Add("AOS60`$$($i.ToString("00"))") } $null = $Services.AddRange(@($MRService, $dixfname)) } else { if ($Aos) { for ($i = 1; $i -lt 100; $i++) { $null = $Services.Add("AOS60`$$($i.ToString("00"))") } } if ($ManagementReporter) { $null = $Services.Add($ManagementReporter) } if ($DIXF) { $null = $Services.Add($dixfname) } } $Services.ToArray() } <# .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-ModuleImport-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 timing logic .DESCRIPTION Invoke timing logic that keeps track of the time spend inside a function .PARAMETER Start Switch to instruct the cmdlet that the starting of measurement .PARAMETER End Switch to 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 Switch to 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 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 Valid options are: "Leaf" "Container" .PARAMETER Create Switch to instruct the cmdlet to create the folder .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", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding()] [OutputType([System.Boolean])] param ( [Parameter(Mandatory = $True, Position = 1 )] [string[]] $Path, [ValidateSet('Leaf', 'Container')] [Parameter(Mandatory = $True, Position = 2 )] [string] $Type, [switch] $Create ) $res = $false $arrList = New-Object -TypeName "System.Collections.ArrayList" foreach ($item in $Path) { Write-PSFMessage -Level Verbose -Message "Testing the path: $item" -Target $item $temp = Test-Path -Path $item -Type $Type if ((!$temp) -and ($Create) -and ($Type -eq "Container")) { Write-PSFMessage -Level Verbose -Message "Creating the path: $item" -Target $item New-Item -Path $item -ItemType Directory -Force -ErrorAction Stop $temp = $true } elseif (!$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)) { Stop-PSFFunction -Message "Stopping because of missing paths." -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 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 GenerateScript Switch to 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(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 1)] [string] $DatabaseServer = "localhost", [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, Position = 2)] [string] $ModelstoreDatabase = "MicrosoftDynamicsAx_model", [Parameter(Mandatory = $false, Position = 3)] [string] $Path = "c:\temp\ax2012.tools", [Parameter(Mandatory = $false, Position = 4)] [string] $Name = "*", [Parameter(Mandatory = $false, Position = 5)] [string] $Id = "*", [Parameter(Mandatory = $false, Position = 6)] [string] $Layer = "*", [Parameter(ParameterSetName = "Generate")] [switch] $GenerateScript ) BEGIN { if (-not ($GenerateScript)) { 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 ($GenerateScript) { $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" $null = Export-AXModel @params } } else { Write-PSFMessage -Level Verbose -Message "Skipping $filenameAxModel in layer $modelLayer" } } Invoke-TimeSignal -End } END { if (-not ($GenerateScript)) { [PSCustomObject]@{ Path = $backupFilePath } } } } <# .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-AxActiveAosConfiguration This will export all the stored details saved into the configuration storage. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxActiveAosConfiguration { [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 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( [Parameter()] [string] $Name = "*", [Parameter()] [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 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:RegistryClient 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 the status of an AX 2012 environment .DESCRIPTION Get the status of AX 2012 services in your environment .PARAMETER ComputerName Name of the computer(s) that you want to work against .PARAMETER All Switch to instruct the cmdlet to include all known AX 2012 services .PARAMETER Aos Switch to instruct the cmdlet to include the AOS service .PARAMETER ManagementReporter Switch to instruct the cmdlet to include the ManagementReporter service .PARAMETER DIXF Switch to instruct the cmdlet to include the DIXF service .EXAMPLE PS C:\> Get-AxEnvironment This will get the status for all the default services from your environment. .NOTES Author: M�tz Jensen (@Splaxi) #> function Get-AxEnvironment { [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [string[]] $ComputerName = @($env:computername), [Parameter(Mandatory = $false, ParameterSetName = 'Default', Position = 2 )] [switch] $All = [switch]::Present, [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") { $All = ![switch]::Present } 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 } $Params = Get-DeepClone $PSBoundParameters if ($Params.ContainsKey("ComputerName")) { $Params.Remove("ComputerName") } $Services = Get-ServiceList @Params $Results = foreach ($server in $ComputerName) { Write-PSFMessage -Level Verbose -Message "Working against: $server - listing services" -Target $server Get-Service -ComputerName $server -Name $Services -ErrorAction SilentlyContinue | Select-Object @{Name = "Server"; Expression = {$Server}}, Name, Status, DisplayName } $Results | Select-Object Server, DisplayName, Status, Name } <# .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 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 CreateParents Switch to instruct the cmdlet to create missing parents on import .PARAMETER NoOptimize Switch to 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 Switch to instruct the cmdlet not to prompt you with anything .PARAMETER GenerateScript Switch to 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". .NOTES Author: M�tz Jensen (@Splaxi) #> Function Import-AxModelV2 { [CmdletBinding()] [OutputType([System.String], ParameterSetName="Generate")] Param( [Parameter(ValueFromPipelineByPropertyName, Mandatory = $false, ValueFromPipeline = $true, Position = 1)] [string] $DatabaseServer = "localhost", [Parameter(ValueFromPipelineByPropertyName, Mandatory = $false, ValueFromPipeline = $true, Position = 2)] [string] $ModelstoreDatabase = "MicrosoftDynamicsAx_model", [Parameter(Mandatory = $false, Position = 3)] [string] $Path = "c:\temp\ax2012.tools", [Parameter(Mandatory = $false, Position = 4)] [ValidateSet("Reject", "Push", "Overwrite")] [string] $ConflictMode = "Overwrite", [switch] $CreateParents, [switch] $NoOptimize, [switch] $NoPrompt, [Parameter(ParameterSetName = "Generate")] [switch] $GenerateScript ) 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") 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 ($GenerateScript) { $arguments = Convert-HashToArgString -InputObject $clonedParams $argumentsSwitch = Convert-HashToArgStringSwitch -InputObject $paramsSwitch "Install-AxModel $($arguments -join ' ') $($argumentsSwitch -join ' ')" } else { #Install-AXModel @clonedParams @paramsSwitch } } Invoke-TimeSignal -End } END { } } <# .SYNOPSIS Aaaaa aaa a aaa a a aa .DESCRIPTION Bbbb b b b b bbbbb bbbb .PARAMETER Path Cccc ccccc ccc cc cc .EXAMPLE PS C:\> Invoke-AxBuild This will work .NOTES Author: M�tz Jensen (@Splaxi) #> function Invoke-AxBuild { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')] [CmdletBinding()] param ( [string] $Path = $Script:ActiveAosBindirectory <# ActiveAosAosPort ActiveAosBindirectory ActiveAosComputername ActiveAosDatabase ActiveAosDatabaseserver ActiveAosInstancename ActiveAosInstanceNumber ActiveAosModelstoredatabase ActiveAosNettcpPort ActiveAosWsdlPort #> ) Get-Variable } <# .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 GenerateScript Switch to instruct the cmdlet to only generate the needed command and not execute it .EXAMPLE PS C:\> Invoke-AxExportModelstore This will execute the cmdlet will 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 Invoke-AxExportModelstore { [CmdletBinding()] [OutputType([System.String], ParameterSetName = "Generate")] Param( [string] $DatabaseServer = "localhost", [string] $ModelstoreDatabase = "MicrosoftDynamicsAx_model", [string] $InstanceName, [string] $Suffix = $((Get-Date).ToString("yyyyMMdd")), [string] $Path = "c:\temp\ax2012.tools", [Parameter(ParameterSetName = "Generate")] [switch] $GenerateScript ) Invoke-TimeSignal -Start if (-not (Test-PathExists -Path $Path -Type Container -Create)) { return } if (-not (Test-PathExists -Path $Script:AxPowerShellModule -Type Leaf)) { return } $null = Import-Module $Script:AxPowerShellModule if ([System.String]::IsNullOrEmpty($InstanceName)) { $InstanceName = "{0}" -f $ModelstoreDatabase.Replace("_model", "") } $ExportPath = Join-Path $Path "$($InstanceName)_$($Suffix)" $params = @{ Server = $DatabaseServer Database = $ModelstoreDatabase File = $ExportPath } if ($GenerateScript) { $arguments = Convert-HashToArgString -InputObject $params "Export-AxModelStore $($arguments -join ' ')" } else { Write-PSFMessage -Level Verbose -Message "Starting the export of the model store" Export-AxModelStore @params } 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 Temporary Switch to instruct the cmdlet to only temporarily override the persisted settings in the configuration storage .EXAMPLE PS C:\> Get-AxAosInstance | Select-Object -First 1 | Set-AxActiveAosConfiguration 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-AxActiveAosConfiguration -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 .NOTES Author: M�tz Jensen (@Splaxi) #> function Set-AxActiveAosConfiguration { [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, [switch] $Temporary ) foreach ($key in $PSBoundParameters.Keys) { $value = $PSBoundParameters.Item($key).ToString() 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' } } "BinDirectory" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.bindirectory' -Value $value if(-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.bindirectory' } } "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' } } "InstanceName" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.instancename' -Value $value if(-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.instancename' } } "DatabaseServer" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.databaseserver' -Value $value if(-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.databaseserver' } } "DatabaseName" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.database' -Value $value if(-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.database' } } "ModelstoreDatabase" { Set-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.modelstoredatabase' -Value $value if(-not $Temporary) { Register-PSFConfig -Module 'ax2012.tools' -Name 'active.aos.modelstoredatabase' } } "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' } } "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' } } "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' } } Default {} } } } <# .SYNOPSIS Start an AX 2012 environment .DESCRIPTION Start an AX 2012 services in your environment .PARAMETER ComputerName Name of the computer(s) that you want to work against .PARAMETER All Switch to instruct the cmdlet to include all known AX 2012 services .PARAMETER Aos Switch to instruct the cmdlet to include the AOS service .PARAMETER ManagementReporter Switch to instruct the cmdlet to include the ManagementReporter service .PARAMETER DIXF Switch to instruct the cmdlet to include the DIXF service .EXAMPLE PS C:\> Start-AxEnvironment This will start all the known AX 2012 services on the machine that you are executing it on. .NOTES Author: M�tz Jensen (@Splaxi) #> function Start-AxEnvironment { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [string[]] $ComputerName = @($env:computername), [Parameter(Mandatory = $false, ParameterSetName = 'Default', Position = 2 )] [switch] $All = [switch]::Present, [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") { $All = ![switch]::Present } 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 } $Params = Get-DeepClone $PSBoundParameters if ($Params.ContainsKey("ComputerName")) { $Params.Remove("ComputerName") } $Services = Get-ServiceList @Params $Results = foreach ($server in $ComputerName) { Write-PSFMessage -Level Verbose -Message "Working against: $server - starting services" -Target $server Get-Service -ComputerName $server -Name $Services -ErrorAction SilentlyContinue | Start-Service -ErrorAction SilentlyContinue } $Results = foreach ($server in $ComputerName) { Write-PSFMessage -Level Verbose -Message "Working against: $server - listing services" -Target $server Get-Service -ComputerName $server -Name $Services -ErrorAction SilentlyContinue | Select-Object @{Name = "Server"; Expression = {$Server}}, Name, Status, DisplayName } $Results | Select-Object Server, DisplayName, Status, Name } <# .SYNOPSIS Stop an AX 2012 environment .DESCRIPTION Stop an AX 2012 services in your environment .PARAMETER ComputerName Name of the computer(s) that you want to work against .PARAMETER All Switch to instruct the cmdlet to include all known AX 2012 services .PARAMETER Aos Switch to instruct the cmdlet to include the AOS service .PARAMETER ManagementReporter Switch to instruct the cmdlet to include the ManagementReporter service .PARAMETER DIXF Switch to instruct the cmdlet to include the DIXF service .EXAMPLE PS C:\> Stop-AxEnvironment This will stop all the known AX 2012 services on the machine that you are executing it on. .NOTES Author: M�tz Jensen (@Splaxi) #> function Stop-AxEnvironment { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [string[]] $ComputerName = @($env:computername), [Parameter(Mandatory = $false, ParameterSetName = 'Default', Position = 2 )] [switch] $All = [switch]::Present, [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") { $All = ![switch]::Present } 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 } $Params = Get-DeepClone $PSBoundParameters if ($Params.ContainsKey("ComputerName")) { $Params.Remove("ComputerName") } $Services = Get-ServiceList @Params $Results = foreach ($server in $ComputerName) { Write-PSFMessage -Level Verbose -Message "Working against: $server - stopping services" -Target $server Get-Service -ComputerName $server -Name $Services -ErrorAction SilentlyContinue | Stop-Service -Force -ErrorAction SilentlyContinue } $Results = foreach ($server in $ComputerName) { Write-PSFMessage -Level Verbose -Message "Working against: $server - listing services" -Target $server Get-Service -ComputerName $server -Name $Services -ErrorAction SilentlyContinue | Select-Object @{Name = "Server"; Expression = {$Server}}, Name, Status, DisplayName } $Results | Select-Object Server, DisplayName, Status, Name } |