diff --git a/.github/workflows/build-manual.yml b/.github/workflows/build-manual.yml new file mode 100644 index 0000000..c9c09c9 --- /dev/null +++ b/.github/workflows/build-manual.yml @@ -0,0 +1,28 @@ +on: + workflow_dispatch: + inputs: + skippublish: + description: "Determines if the publishing to the PowerShell Gallery is skipped" + default: $True + required: false + type: choice + options: + - $False + - $True + skipghrelease: + description: "Determines if creation of a GitHub release is skipped" + default: $False + required: false + type: choice + options: + - $False + - $True + +jobs: + call-tmpl-build-release: + uses: ./.github/workflows/tmpl-build-release.yml + with: + skippublish: ${{ inputs.skippublish }} + skipghrelease: ${{ inputs.skipghrelease }} + secrets: + apikey: ${{ secrets.ApiKey }} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 012bfe7..7b73477 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,20 +5,10 @@ - main jobs: - build: - - runs-on: windows-latest - - steps: - - uses: actions/checkout@v1 - - name: Install Prerequisites - run: .\build\vsts-prerequisites.ps1 - shell: powershell - - name: Validate - run: .\build\vsts-validate.ps1 - shell: powershell - - name: Build - run: .\build\vsts-build.ps1 -ApiKey $env:APIKEY -AutoVersion - shell: powershell - env: - APIKEY: ${{ secrets.ApiKey }} + call-tmpl-build-release: + uses: ./.github/workflows/tmpl-build-release.yml + with: + skippublish: $False + skipghrelease: $False + secrets: + apikey: ${{ secrets.ApiKey }} \ No newline at end of file diff --git a/.github/workflows/tmpl-build-release.yml b/.github/workflows/tmpl-build-release.yml new file mode 100644 index 0000000..336c18a --- /dev/null +++ b/.github/workflows/tmpl-build-release.yml @@ -0,0 +1,63 @@ +# Template for a reusable workflow to build and release a PowerShell module. +# The template expects the following PowerShell scripts in the build folder of the repository root +# that do the actual validated build and release: +# - vsts-prerequisities.ps1 +# - vsts-validate.ps1 +# - vsts-build.ps1 + +on: + workflow_call: + inputs: + autoversion: + description: "Determines if the module's build version number is automatically incremented" + default: $True + required: false + type: string + skippublish: + description: "Determines if the publishing to the PowerShell Gallery is skipped" + default: $False + required: false + type: string + skipghrelease: + description: "Determines if a GitHub release is created" + default: $False + required: false + type: string + secrets: + apikey: + description: "Key for the PowerShell Gallery API" + required: true + +jobs: + build-and-release: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + - name: Install Prerequisites + run: .\build\vsts-prerequisites.ps1 + shell: powershell + - name: Validate + run: .\build\vsts-validate.ps1 + shell: powershell + - name: Build + run: .\build\vsts-build.ps1 -ApiKey ${{ secrets.apikey }} -AutoVersion:${{ inputs.autoversion }} -SkipPublish:${{ inputs.skippublish }} + shell: powershell + + - name: Get version + run: | + $publishDir = Get-Item -Path publish + [version]$version = (Import-PowerShellDataFile -Path "$($publishDir.FullName)\d365bap.tools\d365bap.tools.psd1").ModuleVersion + $githubReleaseVersion = "$($version.Major).$($version.Minor).$($version.Build)" + "VERSION=$githubReleaseVersion" >> $env:GITHUB_ENV + shell: powershell + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + name: ${{ env.VERSION }} + tag_name: ${{ env.VERSION }} + draft: false + prerelease: true + generate_release_notes: true \ No newline at end of file diff --git a/d365bap.tools/d365bap.tools.psd1 b/d365bap.tools/d365bap.tools.psd1 index 44d1d08..53cd879 100644 --- a/d365bap.tools/d365bap.tools.psd1 +++ b/d365bap.tools/d365bap.tools.psd1 @@ -48,11 +48,15 @@ , 'Confirm-BapEnvironmentIntegration' + , 'Export-BapEnvironmentSolution' + , 'Get-BapEnvironment' , 'Get-BapEnvironmentApplicationUser' , 'Get-BapEnvironmentD365App' + , 'Get-BapEnvironmentSolution' + , 'Get-BapEnvironmentUser' , 'Get-BapEnvironmentVirtualEntity' diff --git a/d365bap.tools/functions/Export-BapEnvironmentSolution.ps1 b/d365bap.tools/functions/Export-BapEnvironmentSolution.ps1 new file mode 100644 index 0000000..f9f1302 --- /dev/null +++ b/d365bap.tools/functions/Export-BapEnvironmentSolution.ps1 @@ -0,0 +1,113 @@ + +<# + .SYNOPSIS + Export PowerPlatform / Dataverse Solution from the environment + + .DESCRIPTION + Enables the user to export solutions, on a given environment + + The cmdlet downloads the solution, extracts it and removes unnecessary files + + .PARAMETER EnvironmentId + The id of the environment that you want to work against + + This can be obtained from the Get-BapEnvironment cmdlet + + .PARAMETER SolutionId + The id of the solution that you want to work against + + This can be obtained from the Get-BapEnvironmentSolution cmdlet + + .PARAMETER Path + Path to the location that you want the files to be exported to + + .EXAMPLE + PS C:\> Export-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -SolutionId 3ac10775-0808-42e0-bd23-83b6c714972f -Path "C:\Temp\" + + This will export the solution from the environment. + It will extract the files into the "C:\Temp\" location. + + .NOTES + Author: Mötz Jensen (@Splaxi) +#> +function Export-BapEnvironmentSolution { + [CmdletBinding()] + param ( + [parameter (mandatory = $true)] + [string] $EnvironmentId, + + [parameter (mandatory = $true)] + [string] $SolutionId, + + [parameter (mandatory = $true)] + [string] $Path + ) + + begin { + # Make sure all *BapEnvironment* cmdlets will validate that the environment exists prior running anything. + $envObj = Get-BapEnvironment -EnvironmentId $EnvironmentId | Select-Object -First 1 + + if ($null -eq $envObj) { + $messageString = "The supplied EnvironmentId: $EnvironmentId didn't return any matching environment details. Please verify that the EnvironmentId is correct - try running the Get-BapEnvironment cmdlet." + Write-PSFMessage -Level Host -Message $messageString + Stop-PSFFunction -Message "Stopping because environment was NOT found based on the id." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) + } + + if (Test-PSFFunctionInterrupt) { return } + + $solObj = Get-BapEnvironmentSolution -EnvironmentId $EnvironmentId -SolutionId $SolutionId | Select-Object -First 1 + + if ($null -eq $solObj) { + $messageString = "The supplied SolutionId: $SolutionId didn't return any matching solution from the environment. Please verify that the SolutionId is correct - try running the Get-BapEnvironmentSolution cmdlet." + Write-PSFMessage -Level Host -Message $messageString + Stop-PSFFunction -Message "Stopping because solution was NOT found based on the id." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) + } + } + + process { + if (Test-PSFFunctionInterrupt) { return } + + $tmp = pac org list --filter $EnvironmentId + + Write-PSFMessage -Level Host -Message "$($tmp[0])" + + $found = $false + foreach ($line in $tmp) { + $found = $line -like "*$EnvironmentId*" + + if ($found) { + break + } + } + + if (-not $found) { + $messageString = "It seems that the current pac cli session isn't connected to the correct tenant. Please run the pac auth create --name 'ChangeThis' and make sure to use credentials that have enough privileges." + Write-PSFMessage -Level Host -Message $messageString + Stop-PSFFunction -Message "Stopping because pac cli session is NOT connected to the correct tenant." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) + } + + if (Test-PSFFunctionInterrupt) { return } + + # pac cli is connected to the correct tenant + $pathDownload = [System.IO.Path]::ChangeExtension([System.IO.Path]::GetTempFileName(), 'zip') + + $tmp = pac solution export --name $solObj.SystemName --environment $EnvironmentId --path $pathDownload --overwrite + + if (-not (($tmp | Select-Object -Last 1) -eq "Solution export succeeded.")) { + $messageString = "It seems that export of the solution encountered some kind of error. Please run the cmdlet again in a few minutes." + Write-PSFMessage -Level Host -Message $messageString + Stop-PSFFunction -Message "Stopping because export failed." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) + } + + Expand-Archive -Path $pathDownload -DestinationPath $Path -Force + + # Give the file system time to persis the extracted files. + Start-Sleep -Seconds 2 + + Remove-Item -LiteralPath $(Join-Path -Path $Path -ChildPath "[Content_Types].xml") -ErrorAction SilentlyContinue -Force + } + + end { + + } +} \ No newline at end of file diff --git a/d365bap.tools/functions/Get-BapEnvironmentSolution.ps1 b/d365bap.tools/functions/Get-BapEnvironmentSolution.ps1 new file mode 100644 index 0000000..dd1262e --- /dev/null +++ b/d365bap.tools/functions/Get-BapEnvironmentSolution.ps1 @@ -0,0 +1,160 @@ + +<# + .SYNOPSIS + Get PowerPlatform / Dataverse Solution from the environment + + .DESCRIPTION + Enables the user to list solutions and their meta data, on a given environment + + .PARAMETER EnvironmentId + The id of the environment that you want to work against + + This can be obtained from the Get-BapEnvironment cmdlet + + .PARAMETER SolutionId + The id of the solution that you want to work against + + Leave blank to get all solutions + + .PARAMETER IncludeManaged + Instruct the cmdlet to include all managed solution + + .PARAMETER AsExcelOutput + Instruct the cmdlet to output all details directly to an Excel file + + This makes it easier to deep dive into all the details returned from the API, and makes it possible for the user to persist the current state + + .EXAMPLE + PS C:\> Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 + + This will query the specific environment. + It will only list Unmanaged / NON-Managed solutions. + + Sample output: + + SolutionId Name IsManaged SystemName Description + ---------- ---- --------- ---------- ----------- + fd140aae-4df4-11dd-bd17-0019b9312238 Active Solution False Active Placeholder solutio… + + .EXAMPLE + PS C:\> Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged + + This will query the specific environment. + It will list all solutions. + + Sample output: + + SolutionId Name IsManaged SystemName Description + ---------- ---- --------- ---------- ----------- + 169edc7d-5f1e-4ee4-8b5c-135b3ba82ea3 Access Team True AccessTeam Access Team solution + fd140aae-4df4-11dd-bd17-0019b9312238 Active Solution False Active Placeholder solutio… + 458c32fb-4476-4431-97cb-49cfd069c31d Activities True msdynce_Activities Dynamics 365 worklo… + 7553bb8a-fc5e-424c-9698-113958c28c98 Activities Patch True msdynce_ActivitiesP… Patch for Dynamics … + 3ac10775-0808-42e0-bd23-83b6c714972f ActivitiesInfra Solution Anch… True msft_ActivitiesInfr… ActivitiesInfra Sol… + + .EXAMPLE + PS C:\> Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged + + This will query the specific environment. + It will list all solutions, unmanaged / managed. + + Sample output: + + SolutionId Name IsManaged SystemName Description + ---------- ---- --------- ---------- ----------- + 169edc7d-5f1e-4ee4-8b5c-135b3ba82ea3 Access Team True AccessTeam Access Team solution + fd140aae-4df4-11dd-bd17-0019b9312238 Active Solution False Active Placeholder solutio… + 458c32fb-4476-4431-97cb-49cfd069c31d Activities True msdynce_Activities Dynamics 365 worklo… + 7553bb8a-fc5e-424c-9698-113958c28c98 Activities Patch True msdynce_ActivitiesP… Patch for Dynamics … + 3ac10775-0808-42e0-bd23-83b6c714972f ActivitiesInfra Solution Anch… True msft_ActivitiesInfr… ActivitiesInfra Sol… + + .EXAMPLE + PS C:\> Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged -SolutionId 3ac10775-0808-42e0-bd23-83b6c714972f + + This will query the specific environment. + It will list all solutions, unmanaged / managed. + It will search for the 3ac10775-0808-42e0-bd23-83b6c714972f solution. + + Sample output: + + SolutionId Name IsManaged SystemName Description + ---------- ---- --------- ---------- ----------- + 3ac10775-0808-42e0-bd23-83b6c714972f ActivitiesInfra Solution Anch… True msft_ActivitiesInfr… ActivitiesInfra Sol… + + .EXAMPLE + PS C:\> Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged -AsExcelOutput + + This will query the specific environment. + It will list all solutions, unmanaged / managed. + Will output all details into an Excel file, that will auto open on your machine. + + .NOTES + Author: Mötz Jensen (@Splaxi) +#> +function Get-BapEnvironmentSolution { + [CmdletBinding()] + [OutputType('System.Object[]')] + param ( + [parameter (mandatory = $true)] + [string] $EnvironmentId, + + [string] $SolutionId, + + [switch] $IncludeManaged, + + [switch] $AsExcelOutput + ) + + begin { + # Make sure all *BapEnvironment* cmdlets will validate that the environment exists prior running anything. + $envObj = Get-BapEnvironment -EnvironmentId $EnvironmentId | Select-Object -First 1 + + if ($null -eq $envObj) { + $messageString = "The supplied EnvironmentId: $EnvironmentId didn't return any matching environment details. Please verify that the EnvironmentId is correct - try running the Get-BapEnvironment cmdlet." + Write-PSFMessage -Level Host -Message $messageString + Stop-PSFFunction -Message "Stopping because environment was NOT found based on the id." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) + } + + if (Test-PSFFunctionInterrupt) { return } + + $baseUri = $envObj.LinkedMetaPpacEnvUri + $tokenWebApi = Get-AzAccessToken -ResourceUrl $baseUri + $headersWebApi = @{ + "Authorization" = "Bearer $($tokenWebApi.Token)" + } + } + + process { + if (Test-PSFFunctionInterrupt) { return } + + $localUri = $baseUri + '/api/data/v9.2/solutions' + $search = '?$filter=ismanaged eq false' + + if (-not $IncludeManaged) { + $localUri += $search + } + + $resSolutions = Invoke-RestMethod -Method Get -Uri $localUri -Headers $headersWebApi + + $resCol = @( + foreach ($solObj in $($resSolutions.value | Sort-Object -Property friendlyname)) { + if ((-not [System.String]::IsNullOrEmpty($SolutionId)) -and $solObj.SolutionId -ne $SolutionId) { continue } + + $solObj | Select-PSFObject -TypeName "D365Bap.Tools.Solution" -Property "uniquename as SystemName", + "friendlyname as Name", + * + } + ) + + if ($AsExcelOutput) { + $resCol | Export-Excel + return + } + + $resCol + } + + end { + + } +} \ No newline at end of file diff --git a/d365bap.tools/tests/functions/Export-BapEnvironmentSolution.Tests.ps1 b/d365bap.tools/tests/functions/Export-BapEnvironmentSolution.Tests.ps1 new file mode 100644 index 0000000..7d12739 --- /dev/null +++ b/d365bap.tools/tests/functions/Export-BapEnvironmentSolution.Tests.ps1 @@ -0,0 +1,62 @@ +Describe "Export-BapEnvironmentSolution Unit Tests" -Tag "Unit" { + BeforeAll { + # Place here all things needed to prepare for the tests + } + AfterAll { + # Here is where all the cleanup tasks go + } + + Describe "Ensuring unchanged command signature" { + It "should have the expected parameter sets" { + (Get-Command Export-BapEnvironmentSolution).ParameterSets.Name | Should -Be '__AllParameterSets' + } + + It 'Should have the expected parameter EnvironmentId' { + $parameter = (Get-Command Export-BapEnvironmentSolution).Parameters['EnvironmentId'] + $parameter.Name | Should -Be 'EnvironmentId' + $parameter.ParameterType.ToString() | Should -Be System.String + $parameter.IsDynamic | Should -Be $False + $parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' + $parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' + $parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $True + $parameter.ParameterSets['__AllParameterSets'].Position | Should -Be 0 + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False + } + It 'Should have the expected parameter SolutionId' { + $parameter = (Get-Command Export-BapEnvironmentSolution).Parameters['SolutionId'] + $parameter.Name | Should -Be 'SolutionId' + $parameter.ParameterType.ToString() | Should -Be System.String + $parameter.IsDynamic | Should -Be $False + $parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' + $parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' + $parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $True + $parameter.ParameterSets['__AllParameterSets'].Position | Should -Be 1 + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False + } + It 'Should have the expected parameter Path' { + $parameter = (Get-Command Export-BapEnvironmentSolution).Parameters['Path'] + $parameter.Name | Should -Be 'Path' + $parameter.ParameterType.ToString() | Should -Be System.String + $parameter.IsDynamic | Should -Be $False + $parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' + $parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' + $parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $True + $parameter.ParameterSets['__AllParameterSets'].Position | Should -Be 2 + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False + } + } + + Describe "Testing parameterset __AllParameterSets" { + <# + __AllParameterSets -EnvironmentId -SolutionId -Path + __AllParameterSets -EnvironmentId -SolutionId -Path + #> + } + +} \ No newline at end of file diff --git a/d365bap.tools/tests/functions/Get-BapEnvironmentSolution.Tests.ps1 b/d365bap.tools/tests/functions/Get-BapEnvironmentSolution.Tests.ps1 new file mode 100644 index 0000000..aedcc61 --- /dev/null +++ b/d365bap.tools/tests/functions/Get-BapEnvironmentSolution.Tests.ps1 @@ -0,0 +1,75 @@ +Describe "Get-BapEnvironmentSolution Unit Tests" -Tag "Unit" { + BeforeAll { + # Place here all things needed to prepare for the tests + } + AfterAll { + # Here is where all the cleanup tasks go + } + + Describe "Ensuring unchanged command signature" { + It "should have the expected parameter sets" { + (Get-Command Get-BapEnvironmentSolution).ParameterSets.Name | Should -Be '__AllParameterSets' + } + + It 'Should have the expected parameter EnvironmentId' { + $parameter = (Get-Command Get-BapEnvironmentSolution).Parameters['EnvironmentId'] + $parameter.Name | Should -Be 'EnvironmentId' + $parameter.ParameterType.ToString() | Should -Be System.String + $parameter.IsDynamic | Should -Be $False + $parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' + $parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' + $parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $True + $parameter.ParameterSets['__AllParameterSets'].Position | Should -Be 0 + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False + } + It 'Should have the expected parameter SolutionId' { + $parameter = (Get-Command Get-BapEnvironmentSolution).Parameters['SolutionId'] + $parameter.Name | Should -Be 'SolutionId' + $parameter.ParameterType.ToString() | Should -Be System.String + $parameter.IsDynamic | Should -Be $False + $parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' + $parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' + $parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].Position | Should -Be 1 + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False + } + It 'Should have the expected parameter IncludeManaged' { + $parameter = (Get-Command Get-BapEnvironmentSolution).Parameters['IncludeManaged'] + $parameter.Name | Should -Be 'IncludeManaged' + $parameter.ParameterType.ToString() | Should -Be System.Management.Automation.SwitchParameter + $parameter.IsDynamic | Should -Be $False + $parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' + $parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' + $parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].Position | Should -Be -2147483648 + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False + } + It 'Should have the expected parameter AsExcelOutput' { + $parameter = (Get-Command Get-BapEnvironmentSolution).Parameters['AsExcelOutput'] + $parameter.Name | Should -Be 'AsExcelOutput' + $parameter.ParameterType.ToString() | Should -Be System.Management.Automation.SwitchParameter + $parameter.IsDynamic | Should -Be $False + $parameter.ParameterSets.Keys | Should -Be '__AllParameterSets' + $parameter.ParameterSets.Keys | Should -Contain '__AllParameterSets' + $parameter.ParameterSets['__AllParameterSets'].IsMandatory | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].Position | Should -Be -2147483648 + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipeline | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromPipelineByPropertyName | Should -Be $False + $parameter.ParameterSets['__AllParameterSets'].ValueFromRemainingArguments | Should -Be $False + } + } + + Describe "Testing parameterset __AllParameterSets" { + <# + __AllParameterSets -EnvironmentId + __AllParameterSets -EnvironmentId -SolutionId -IncludeManaged -AsExcelOutput + #> + } + +} \ No newline at end of file diff --git a/d365bap.tools/xml/d365bap.tools.Format.ps1xml b/d365bap.tools/xml/d365bap.tools.Format.ps1xml index c295cc9..4212e66 100644 --- a/d365bap.tools/xml/d365bap.tools.Format.ps1xml +++ b/d365bap.tools/xml/d365bap.tools.Format.ps1xml @@ -435,5 +435,51 @@ + + D365Bap.Tools.Solution + + D365Bap.Tools.Solution + + + + + 36 + + + 30 + + + 9 + + + 20 + + + 20 + + + + + + + SolutionId + + + Name + + + IsManaged + + + SystemName + + + Description + + + + + + \ No newline at end of file diff --git a/docs/Export-BapEnvironmentSolution.md b/docs/Export-BapEnvironmentSolution.md new file mode 100644 index 0000000..7def8a7 --- /dev/null +++ b/docs/Export-BapEnvironmentSolution.md @@ -0,0 +1,96 @@ +--- +external help file: d365bap.tools-help.xml +Module Name: d365bap.tools +online version: +schema: 2.0.0 +--- + +# Export-BapEnvironmentSolution + +## SYNOPSIS +Export PowerPlatform / Dataverse Solution from the environment + +## SYNTAX + +``` +Export-BapEnvironmentSolution [-EnvironmentId] [-SolutionId] [-Path] + [] +``` + +## DESCRIPTION +Enables the user to export solutions, on a given environment + +The cmdlet downloads the solution, extracts it and removes unnecessary files + +## EXAMPLES + +### EXAMPLE 1 +``` +Export-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -SolutionId 3ac10775-0808-42e0-bd23-83b6c714972f -Path "C:\Temp\" +``` + +This will export the solution from the environment. +It will extract the files into the "C:\Temp\" location. + +## PARAMETERS + +### -EnvironmentId +The id of the environment that you want to work against + +This can be obtained from the Get-BapEnvironment cmdlet + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SolutionId +The id of the solution that you want to work against + +This can be obtained from the Get-BapEnvironmentSolution cmdlet + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Path +Path to the location that you want the files to be exported to + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES +Author: Mötz Jensen (@Splaxi) + +## RELATED LINKS diff --git a/docs/Get-BapEnvironmentSolution.md b/docs/Get-BapEnvironmentSolution.md new file mode 100644 index 0000000..b58549f --- /dev/null +++ b/docs/Get-BapEnvironmentSolution.md @@ -0,0 +1,177 @@ +--- +external help file: d365bap.tools-help.xml +Module Name: d365bap.tools +online version: +schema: 2.0.0 +--- + +# Get-BapEnvironmentSolution + +## SYNOPSIS +Get PowerPlatform / Dataverse Solution from the environment + +## SYNTAX + +``` +Get-BapEnvironmentSolution [-EnvironmentId] [[-SolutionId] ] [-IncludeManaged] + [-AsExcelOutput] [] +``` + +## DESCRIPTION +Enables the user to list solutions and their meta data, on a given environment + +## EXAMPLES + +### EXAMPLE 1 +``` +Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 +``` + +This will query the specific environment. +It will only list Unmanaged / NON-Managed solutions. + +Sample output: + +SolutionId Name IsManaged SystemName Description +---------- ---- --------- ---------- ----------- +fd140aae-4df4-11dd-bd17-0019b9312238 Active Solution False Active Placeholder solutio… + +### EXAMPLE 2 +``` +Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged +``` + +This will query the specific environment. +It will list all solutions. + +Sample output: + +SolutionId Name IsManaged SystemName Description +---------- ---- --------- ---------- ----------- +169edc7d-5f1e-4ee4-8b5c-135b3ba82ea3 Access Team True AccessTeam Access Team solution +fd140aae-4df4-11dd-bd17-0019b9312238 Active Solution False Active Placeholder solutio… +458c32fb-4476-4431-97cb-49cfd069c31d Activities True msdynce_Activities Dynamics 365 worklo… +7553bb8a-fc5e-424c-9698-113958c28c98 Activities Patch True msdynce_ActivitiesP… Patch for Dynamics … +3ac10775-0808-42e0-bd23-83b6c714972f ActivitiesInfra Solution Anch… True msft_ActivitiesInfr… ActivitiesInfra Sol… + +### EXAMPLE 3 +``` +Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged +``` + +This will query the specific environment. +It will list all solutions, unmanaged / managed. + +Sample output: + +SolutionId Name IsManaged SystemName Description +---------- ---- --------- ---------- ----------- +169edc7d-5f1e-4ee4-8b5c-135b3ba82ea3 Access Team True AccessTeam Access Team solution +fd140aae-4df4-11dd-bd17-0019b9312238 Active Solution False Active Placeholder solutio… +458c32fb-4476-4431-97cb-49cfd069c31d Activities True msdynce_Activities Dynamics 365 worklo… +7553bb8a-fc5e-424c-9698-113958c28c98 Activities Patch True msdynce_ActivitiesP… Patch for Dynamics … +3ac10775-0808-42e0-bd23-83b6c714972f ActivitiesInfra Solution Anch… True msft_ActivitiesInfr… ActivitiesInfra Sol… + +### EXAMPLE 4 +``` +Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged -SolutionId 3ac10775-0808-42e0-bd23-83b6c714972f +``` + +This will query the specific environment. +It will list all solutions, unmanaged / managed. +It will search for the 3ac10775-0808-42e0-bd23-83b6c714972f solution. + +Sample output: + +SolutionId Name IsManaged SystemName Description +---------- ---- --------- ---------- ----------- +3ac10775-0808-42e0-bd23-83b6c714972f ActivitiesInfra Solution Anch… True msft_ActivitiesInfr… ActivitiesInfra Sol… + +### EXAMPLE 5 +``` +Get-BapEnvironmentSolution -EnvironmentId eec2c11a-a4c7-4e1d-b8ed-f62acc9c74c6 -IncludeManaged -AsExcelOutput +``` + +This will query the specific environment. +It will list all solutions, unmanaged / managed. +Will output all details into an Excel file, that will auto open on your machine. + +## PARAMETERS + +### -EnvironmentId +The id of the environment that you want to work against + +This can be obtained from the Get-BapEnvironment cmdlet + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SolutionId +The id of the solution that you want to work against + +Leave blank to get all solutions + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IncludeManaged +Instruct the cmdlet to include all managed solution + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -AsExcelOutput +Instruct the cmdlet to output all details directly to an Excel file + +This makes it easier to deep dive into all the details returned from the API, and makes it possible for the user to persist the current state + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES +Author: Mötz Jensen (@Splaxi) + +## RELATED LINKS diff --git a/learning/notebooks/ReadMe.md b/learning/notebooks/ReadMe.md new file mode 100644 index 0000000..3684fa3 --- /dev/null +++ b/learning/notebooks/ReadMe.md @@ -0,0 +1,19 @@ +# About the notebooks + +This folder contains [Jupyter](https://jupyter.org/) notebooks to learn in an interactive way how to use the d365bap.tools PowerShell module. + +The notebooks can be viewed directly on GitHub or other clients that support viewing the Jupyter notebook format. It is however recommended to run them interactively. The easiest way to do so is to use the preconfigured GitHub Codespaces environment: + +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/d365collaborative/d365bap.tools) + +To run the notebooks locally, open them in Visual Studio Code with the [Polyglot](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode) extension installed. + +## Notebooks + +### Getting started +- [Get started](get-started.ipynb) - Learn how to install the module and get started with the different cmdlets / functions available in the module. +- [Learn commands](learn-commands.ipynb) - Learn about the different cmdlets / functions available in the module. + +### Scenarios +- [Confirm integration](confirm-integration.ipynb) - Confirm the integration of an environment with a Dynamics 365 Finance and Operations app. +- [Install app updates](install-appupdates.ipynb) - Install updates for the apps installed in an environment. diff --git a/learning/notebooks/confirm-integration.ipynb b/learning/notebooks/confirm-integration.ipynb new file mode 100644 index 0000000..3fe6b92 --- /dev/null +++ b/learning/notebooks/confirm-integration.ipynb @@ -0,0 +1,111 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Confirm integration\n", + "\n", + "This short notebook will teach you how to use the `Confirm-BapEnvironmentIntegration` cmdlet.\n", + "\n", + "## Authentication\n", + "\n", + "First, you need to authorize your Azure account. Review [Get started](get-started.ipynb) for more details." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[93mWARNING: To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code ABCD1234 to authenticate.\u001b[0m\n", + "\n", + "\u001b[32;1;3mAccount \u001b[0m \u001b[32;1;3mSubscriptionName \u001b[0m \u001b[32;1;3mTenantId \u001b[0m \u001b[32;1;3mEnvi\u001b[0m\n", + "\u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3mronm\u001b[0m\n", + "\u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3ment\u001b[0m\n", + "\u001b[32;1m------- \u001b[0m \u001b[32;1m---------------- \u001b[0m \u001b[32;1m-------- \u001b[0m \u001b[32;1m----\u001b[0m\n", + "me@mycompany.com MySubscriptionName abcdefgh-1234-5678-90ab-cdefghijklmn Azu…\n", + "\n" + ] + } + ], + "source": [ + "Connect-AzAccount -UseDeviceAuthentication" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Confirm-BapEnvironmentIntegration\n", + "\n", + "In the `Get-BapEnvironmentVirtualEntity` section of [Learn commands](learn-commands.ipynb), you learned how to filter the output of `Get-BapEnvironment` by the `LinkedAppLcsEnvUri` property to get only environments that have a Dynamics 365 Finance and Operations app. But just because the `LinkedAppLcsEnvUri` property has a value does not necessarily mean that the environment is fully integrated with the app. To verify the full integration, you can use the `Confirm-BapEnvironmentIntegration` cmdlet." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 integrated environments\n", + "\n", + "\u001b[32;1mLinkedAppLcsEnvId \u001b[0m\u001b[32;1m LinkedAppLcsEnvUri \u001b[0m\u001b[32;1m IsUnifiedDa\u001b[0m\n", + "\u001b[32;1m \u001b[0m\u001b[32;1m \u001b[0m\u001b[32;1m tabase\u001b[0m\n", + "\u001b[32;1m----------------- \u001b[0m \u001b[32;1m------------------ \u001b[0m \u001b[32;1m-----------\u001b[0m\n", + "11111111-1111-1111-1111-111111111111 https://my1stenvironment.sandbox.operations.dynam… False \n", + "22222222-2222-2222-2222-222222222222 https://my2ndenvironment.sandbox.operations.dynam… False \n", + "33333333-3333-3333-3333-333333333333 https://my3rdenvironment.sandbox.operations.dynam… False \n", + "\n" + ] + } + ], + "source": [ + "$fnoLinkedEnvironments = (Get-BapEnvironment | Where-Object { $_.LinkedappLcsEnvUri -ne $null })\n", + "$fnoIntegratedEnvironments = @()\n", + "foreach ($environment in $fnoLinkedEnvironments) {\n", + " $confirmation = Confirm-BapEnvironmentIntegration -EnvironmentId $environment.Id\n", + " if ($confirmation) {\n", + " $fnoIntegratedEnvironments += $confirmation\n", + " }\n", + "}\n", + "Write-Host \"$($fnoIntegratedEnvironments.Count) integrated environments\"\n", + "$fnoIntegratedEnvironments" + ] + } + ], + "metadata": { + "language_info": { + "name": "csharp" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/learning/notebooks/get-started.ipynb b/learning/notebooks/get-started.ipynb index f8a9203..5d45731 100644 --- a/learning/notebooks/get-started.ipynb +++ b/learning/notebooks/get-started.ipynb @@ -463,6 +463,17 @@ "source": [ "code /tmp/tmpwJFNYb.xlsx" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Next steps\n", + "\n", + "Now that you have successfully run your first cmdlet, you can continue with the other notebooks in this folder to learn more about the d365bap.tools module and how to use it to manage your Dataverse environments.\n", + "\n", + "Take a look at [Learn commands](./learn-commands.ipynb) to get an introduction to other cmdlets of the module." + ] } ], "metadata": { diff --git a/learning/notebooks/install-appupdates.ipynb b/learning/notebooks/install-appupdates.ipynb new file mode 100644 index 0000000..bd43c0f --- /dev/null +++ b/learning/notebooks/install-appupdates.ipynb @@ -0,0 +1,139 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install app updates\n", + "\n", + "In this notebook, you will learn how to use d365bap.tools to determine the apps of an environment that have updates available and install them.\n", + "\n", + "## Authentication\n", + "\n", + "First, you need to authorize your Azure account. Review [Get started](get-started.ipynb) for more details." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[93mWARNING: To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code ABCD1234 to authenticate.\u001b[0m\n", + "\n", + "\u001b[32;1;3mAccount \u001b[0m \u001b[32;1;3mSubscriptionName \u001b[0m \u001b[32;1;3mTenantId \u001b[0m \u001b[32;1;3mEnvi\u001b[0m\n", + "\u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3mronm\u001b[0m\n", + "\u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3ment\u001b[0m\n", + "\u001b[32;1m------- \u001b[0m \u001b[32;1m---------------- \u001b[0m \u001b[32;1m-------- \u001b[0m \u001b[32;1m----\u001b[0m\n", + "me@mycompany.com MySubscriptionName abcdefgh-1234-5678-90ab-cdefghijklmn Azu…\n", + "\n" + ] + } + ], + "source": [ + "Connect-AzAccount -UseDeviceAuthentication" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get apps with available updates\n", + "\n", + "Next, you will use the `Get-BapEnvironment` and `Get-BapEnvironmentD365App` cmdlets to get the apps of an environment that have updates available." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "11111111-2222-3333-4444-555555555555\n" + ] + } + ], + "source": [ + "$environments = Get-BapEnvironment\n", + "$appIds = @(Get-BapEnvironmentD365App -EnvironmentId $environments[0].Id -InstallState Installed -UpdatesOnly | Select-Object -ExpandProperty PackageId)\n", + "$appIds[0] # Output the first app id that has an update available to make sure there is at least one" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Invoke-BapEnvironmentInstallD365App\n", + "\n", + "Finally, you will use the `Invoke-BapEnvironmentInstallD365App` cmdlet to install the update." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[32;1mstatus \u001b[0m\u001b[32;1m createdDateTime \u001b[0m\u001b[32;1m lastActionDateTime \u001b[0m\u001b[32;1m error\u001b[0m\u001b[32;1m statusMessage\u001b[0m\u001b[32;1m operationId\u001b[0m\n", + "\u001b[32;1m------ \u001b[0m \u001b[32;1m--------------- \u001b[0m \u001b[32;1m------------------ \u001b[0m \u001b[32;1m-----\u001b[0m \u001b[32;1m-------------\u001b[0m \u001b[32;1m-----------\u001b[0m\n", + "Running 3/5/2024 5:00:33 PM 3/5/2024 5:00:43 PM 052034b9-167b-4ac5-ba12-288b8d…\n", + "\n", + "\u001b[32;1mstatus \u001b[0m\u001b[32;1m createdDateTime \u001b[0m\u001b[32;1m lastActionDateTime \u001b[0m\u001b[32;1m error\u001b[0m\u001b[32;1m statusMessage\u001b[0m\u001b[32;1m operationId\u001b[0m\n", + "\u001b[32;1m------ \u001b[0m \u001b[32;1m--------------- \u001b[0m \u001b[32;1m------------------ \u001b[0m \u001b[32;1m-----\u001b[0m \u001b[32;1m-------------\u001b[0m \u001b[32;1m-----------\u001b[0m\n", + "Succeeded 3/5/2024 5:00:33 PM 3/5/2024 5:02:31 PM 052034b9-167b-4ac5-ba12-288b…\n", + "\n" + ] + } + ], + "source": [ + "Invoke-BapEnvironmentInstallD365App -EnvironmentId $environments[0].Id -PackageId $appIds[0]" + ] + } + ], + "metadata": { + "language_info": { + "name": "csharp" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/learning/notebooks/learn-commands.ipynb b/learning/notebooks/learn-commands.ipynb new file mode 100644 index 0000000..2d3355e --- /dev/null +++ b/learning/notebooks/learn-commands.ipynb @@ -0,0 +1,616 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Learn commands\n", + "\n", + "The d365bap.tools module contains multiple cmdlets. Use this notebook to learn how to get to know them.\n", + "\n", + "If this is your first time using this module, take a look at the [Get Started](get-started.ipynb) notebook.\n", + "\n", + "# List the available commands\n", + "\n", + "To list the available commands, use the `Get-Command` cmdlet." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[32;1mCommandType \u001b[0m\u001b[32;1m Name \u001b[0m\u001b[32;1m Version \u001b[0m\u001b[32;1m Source\u001b[0m\n", + "\u001b[32;1m----------- \u001b[0m \u001b[32;1m---- \u001b[0m \u001b[32;1m------- \u001b[0m \u001b[32;1m------\u001b[0m\n", + "Function Compare-BapEnvironmentD365App 0.0.14 d365bap.tools\n", + "Function Compare-BapEnvironmentUser 0.0.14 d365bap.tools\n", + "Function Compare-BapEnvironmentVirtualEntity 0.0.14 d365bap.tools\n", + "Function Confirm-BapEnvironmentIntegration 0.0.14 d365bap.tools\n", + "Function Get-BapEnvironment 0.0.14 d365bap.tools\n", + "Function Get-BapEnvironmentApplicationUser 0.0.14 d365bap.tools\n", + "Function Get-BapEnvironmentD365App 0.0.14 d365bap.tools\n", + "Function Get-BapEnvironmentUser 0.0.14 d365bap.tools\n", + "Function Get-BapEnvironmentVirtualEntity 0.0.14 d365bap.tools\n", + "Function Invoke-BapEnvironmentInstallD365App 0.0.14 d365bap.tools\n", + "Function Set-BapEnvironmentVirtualEntity 0.0.14 d365bap.tools\n", + "Function Update-BapEnvironmentVirtualEntityMetadata 0.0.14 d365bap.tools\n", + "\n" + ] + } + ], + "source": [ + "Get-Command -Module d365bap.tools" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Learn more about a cmdlet\n", + "\n", + "The names of the cmdlets follow the conventions for using a [PowerShell verb](https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands) as the first part of the name. If you are familiar with that convention, you can already guess from the name alone a lot about the purpose of a cmdlet.\n", + "\n", + "To learn more about a cmdlet, you can use the `Get-Help` cmdlet, which was introduced in [Get Started](get-started.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "NAME\n", + " Get-BapEnvironmentUser\n", + " \n", + "SYNOPSIS\n", + " Get users from environment\n", + " \n", + " \n", + "SYNTAX\n", + " Get-BapEnvironmentUser [-EnvironmentId] [-IncludeAppIds] [-AsExcelOutput] \n", + " []\n", + " \n", + " \n", + "DESCRIPTION\n", + " Enables the user to fetch all users from the environment\n", + " \n", + " Utilizes the built-in \"systemusers\" OData entity\n", + " \n", + " Allows the user to include all users, based on those who has the ApplicationId property filled\n", + " \n", + "\n", + "RELATED LINKS\n", + "\n", + "REMARKS\n", + " To see the examples, type: \"Get-Help Get-BapEnvironmentUser -Examples\"\n", + " For more information, type: \"Get-Help Get-BapEnvironmentUser -Detailed\"\n", + " For technical information, type: \"Get-Help Get-BapEnvironmentUser -Full\"\n", + "\n", + "\n" + ] + } + ], + "source": [ + "Get-Help Get-BapEnvironmentUser" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Authenticate\n", + "\n", + "Remember that in order to run a cmdlet, you must authenticate your Azure account first with `Connect-AzAccount`. Review [Get Started](get-started.ipynb) on how to do this. For the purpose of this notebook, the following cell is provided to do the authentication. Feel free to change it to suit your needs." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[93mWARNING: To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code ABCD1234 to authenticate.\u001b[0m\n", + "\n", + "\u001b[32;1;3mAccount \u001b[0m \u001b[32;1;3mSubscriptionName \u001b[0m \u001b[32;1;3mTenantId \u001b[0m \u001b[32;1;3mEnvi\u001b[0m\n", + "\u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3mronm\u001b[0m\n", + "\u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3m \u001b[0m \u001b[32;1;3ment\u001b[0m\n", + "\u001b[32;1m------- \u001b[0m \u001b[32;1m---------------- \u001b[0m \u001b[32;1m-------- \u001b[0m \u001b[32;1m----\u001b[0m\n", + "me@mycompany.com MySubscriptionName abcdefgh-1234-5678-90ab-cdefghijklmn Azu…\n", + "\n" + ] + } + ], + "source": [ + "Connect-AzAccount -UseDeviceAuthentication" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Get cmdlets\n", + "\n", + "The `Get` cmdlets are used to retrieve information about your business applications. They are often easy to use and provide interesting output, which makes them good candidates to explore first.\n", + "\n", + "## Get-BapEnvironment and Get-BapEnvironmentUser\n", + "\n", + "In [Get Started](get-started.ipynb) you already learned about the `Get-BapEnvironment` cmdlet. The information from that can be used for other cmdlets. `Get-BapEnvironmentUser` requires an `-EnvironmentId` parameter to list the users of an environment. The following cell shows how the id of the first environment returned by `Get-BapEnvironment` is used with the `Get-BapEnvironmentUser` cmdlet.\n", + "\n", + "Remember that you can use the `-AsExcelOutput` parameter to get the list of users as an Excel file." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[32;1mEmail \u001b[0m\u001b[32;1m Name \u001b[0m\u001b[32;1m AppId \u001b[0m\u001b[32;1m Systemuserid\u001b[0m\n", + "\u001b[32;1m----- \u001b[0m \u001b[32;1m---- \u001b[0m \u001b[32;1m----- \u001b[0m \u001b[32;1m------------\u001b[0m\n", + " SYSTEM abcdefgh-1234-5…\n", + " INTEGRATION abcdefgh-1234-5…\n", + "365Administrator@mycompany.com 365 Admin abcdefgh-1234-5…\n", + "crmoln@microsoft.com Support User abcdefgh-1234-5…\n", + "crmoln2@microsoft.com Delegated Admin abcdefgh-1234-5…\n", + "\n" + ] + } + ], + "source": [ + "$environments = Get-BapEnvironment\n", + "$firstEnvironmentId = $environments[0].Id\n", + "Get-BapEnvironmentUser -EnvironmentId $firstEnvironmentId" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get-BapEnvironmentApplicationUser\n", + "\n", + "In a similar fashion, you can use the `Get-BapEnvironmentApplicationUser` cmdlet to list all the application users of an environment." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[32;1mAppId \u001b[0m\u001b[32;1m AppName \u001b[0m\u001b[32;1m ApplicationUserId\u001b[0m\n", + "\u001b[32;1m----- \u001b[0m \u001b[32;1m------- \u001b[0m \u001b[32;1m----------------- \u001b[0m\n", + "abcdefgh-1234-5678-90ab-cdefghijklmn AppDeploymentOrchestration abcdefgh-1234-5678-90ab-cdefg…\n", + "\n" + ] + } + ], + "source": [ + "Get-BapEnvironmentApplicationUser -EnvironmentId $firstEnvironmentId" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get-BapEnvironmentD365App\n", + "\n", + "Moving on from users, the `Get-BapEnvironmentD365App` cmdlet lists all the solutions of an environment. This includes the solutions that make up the Dynamics 365 business applications." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[32;1mPackageId \u001b[0m\u001b[32;1m PackageName \u001b[0m\u001b[32;1m AvailableVersion \u001b[0m\u001b[32;1m InstalledVe\u001b[0m\n", + "\u001b[32;1m \u001b[0m\u001b[32;1m \u001b[0m\u001b[32;1m \u001b[0m\u001b[32;1m rsion\u001b[0m\n", + "\u001b[32;1m--------- \u001b[0m \u001b[32;1m----------- \u001b[0m \u001b[32;1m---------------- \u001b[0m \u001b[32;1m-----------\u001b[0m\n", + "abcdefgh-1234-5678-90ab-cdefghijklmn msdyn_ExportControlAnchor 1.0.2601.2 N/A \n", + "\n" + ] + } + ], + "source": [ + "Get-BapEnvironmentD365App -EnvironmentId $firstEnvironmentId" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Get-BapEnvironmentVirtualEntity\n", + "\n", + "The `Get-BapEnvironmentVirtualEntity` cmdlet lists all the virtual entities of an environment. Without any filters, this command can take a few minutes to complete. For demonstration purposes, the following cell uses a filter to only return virtual entities that contain the text \"cust\" in their name. But feel free to remove that filter or use your own filter expression.\n", + "\n", + "Note that this cmdlet requires a Dynamics 365 Finance and Operations app as part of the environment. You can filter the result of `Get-BapEnvironment` to only those that have a value for the `LinkedAppLcsEnvUri` property." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[32;1mEntityName \u001b[0m\u001b[32;1m IsVisible\u001b[0m\u001b[32;1m ChangeTrackingEnabled\u001b[0m\u001b[32;1m EntityGuid\u001b[0m\n", + "\u001b[32;1m---------- \u001b[0m \u001b[32;1m---------\u001b[0m \u001b[32;1m---------------------\u001b[0m \u001b[32;1m----------\u001b[0m\n", + "BankCustPaymModeBankAccountsE… False False 00005ea6-0000-0000-6505-005001000000\n", + "BusinessDocumentCustPaymSched… False False 00005ea6-0000-0000-f201-005001000000\n", + "BusinessDocumentCustTransEnti… False False 00005ea6-0000-0000-a117-005001000000\n", + "BusinessDocumentCustVendCredi… False False 00005ea6-0000-0000-7917-005001000000\n", + "CredManCreditLimitCustGroupEn… False False 00005ea6-0000-0000-4e09-005001000000\n", + "CredManCreditLimitCustGroupLi… False False 00005ea6-0000-0000-4909-005001000000\n", + "CredManCustBalanceStatEntity False False 00005ea6-0000-0000-4709-005001000000\n", + "CredManCustGuaranteeInsurance… False False 00005ea6-0000-0000-4a09-005001000000\n", + "CredManCustRiskScoreEntity False False 00005ea6-0000-0000-4d09-005001000000\n", + "CustAdvancedInvoiceEntity False False 00005ea6-0000-0000-7007-005001000000\n", + "CustAdvancedInvoiceItemsEntity False False 00005ea6-0000-0000-8a07-005001000000\n", + "CustAgingEntity False False 00005ea6-0000-0000-e805-005001000000\n", + "CustBillingClassificationCode… False False 00005ea6-0000-0000-1d03-005001000000\n", + "CustBillingClassificationEnti… False False 00005ea6-0000-0000-5703-005001000000\n", + "CustBillingCodeAccountingDist… False False 00005ea6-0000-0000-a602-005001000000\n", + "CustBillingCodeCustomFieldEnt… False False 00005ea6-0000-0000-ff07-005001000000\n", + "CustBillingCodeRateEntity False False 00005ea6-0000-0000-8806-005001000000\n", + "CustBillingCodeVersionEntity False False 00005ea6-0000-0000-ad05-005001000000\n", + "CustChargeCustomerGroupEntity False False 00005ea6-0000-0000-7817-005001000000\n", + "CustCollectionAgencyFeeEntity False False 00005ea6-0000-0000-4506-005001000000\n", + "CustCollectionAgencyGracePeri… False False 00005ea6-0000-0000-0707-005001000000\n", + "CustCollectionLetterCourseCds… False False 00005ea6-0000-0000-0508-005001000000\n", + "CustConsInvoiceEntity False False 00005ea6-0000-0000-3f07-005001000000\n", + "CustCustomerBaseEntity False False 00005ea6-0000-0000-4102-005001000000\n", + "CustCustomerEntity False False 00005ea6-0000-0000-4906-005001000000\n", + "CustCustomerGroupEntity False False 00005ea6-0000-0000-b904-005001000000\n", + "CustCustomerPaymentFineCodeEn… False False 00005ea6-0000-0000-7f08-005001000000\n", + "CustCustomerPostingProfileHea… False False 00005ea6-0000-0000-bf04-005001000000\n", + "CustCustomerPostingProfileLin… False False 00005ea6-0000-0000-6703-005001000000\n", + "CustCustomerReasonEntity False False 00005ea6-0000-0000-7c08-005001000000\n", + "CustCustomerStatisticsGroupEn… False False 00005ea6-0000-0000-e105-005001000000\n", + "CustCustomerV2Entity False False 00005ea6-0000-0000-c803-005001000000\n", + "CustCustomerV3Entity False False 00005ea6-0000-0000-3a03-005001000000\n", + "CustCustomFieldEntity False False 00005ea6-0000-0000-7f07-005001000000\n", + "CustDirectDebitMandateEntity False False 00005ea6-0000-0000-f705-005001000000\n", + "CustEInvoiceErrorCodeEntity False False 00005ea6-0000-0000-7b03-005001000000\n", + "CustEinvoiceIntegrationTypeEn… False False 00005ea6-0000-0000-3f05-005001000000\n", + "CustEntryCertificateJournalEn… False False 00005ea6-0000-0000-0d02-005001000000\n", + "CustIntentLetterEntity_IT False False 00005ea6-0000-0000-d504-005001000000\n", + "CustInterestCdsEntity False False 00005ea6-0000-0000-4701-005001000000\n", + "CustInvoiceJournalHeaderEntity False False 00005ea6-0000-0000-4406-005001000000\n", + "CustInvoiceJournalLineEntity False False 00005ea6-0000-0000-7202-005001000000\n", + "CustomChequeLayoutEntity False False 00005ea6-0000-0000-4a06-005001000000\n", + "CustomCounterAgentEntity False False 00005ea6-0000-0000-6e08-005001000000\n", + "CustomerAttachmentsEntity False False 00005ea6-0000-0000-a706-005001000000\n", + "CustomerAttachmentsV2Entity False False 00005ea6-0000-0000-2404-005001000000\n", + "CustomerAttributeValueEntity False False 00005ea6-0000-0000-ba05-005001000000\n", + "CustomerBankAccountEntity False False 00005ea6-0000-0000-d401-005001000000\n", + "CustomerBOEProtestSetttleEnti… False False 00005ea6-0000-0000-2b05-005001000000\n", + "CustomerFiscalDocTextReferenc… False False 00005ea6-0000-0000-f003-005001000000\n", + "CustomerFiscalDocumentTextEnt… False False 00005ea6-0000-0000-d802-005001000000\n", + "CustomerHierarchyCatalogEntity False False 00005ea6-0000-0000-9117-005001000000\n", + "CustomerHierarchyEntity False False 00005ea6-0000-0000-0208-005001000000\n", + "CustomerHierarchyNodeEntity False False 00005ea6-0000-0000-2402-005001000000\n", + "CustomerParametersEntity False False 00005ea6-0000-0000-5d04-005001000000\n", + "CustomerPaymentFeeEntity False False 00005ea6-0000-0000-5f01-005001000000\n", + "CustomerPaymentJournalFeeEnti… False False 00005ea6-0000-0000-ef02-005001000000\n", + "CustomerPaymentJournalHeaderE… False False 00005ea6-0000-0000-2507-005001000000\n", + "CustomerPaymentJournalLineEnt… False False 00005ea6-0000-0000-5506-005001000000\n", + "CustomerPaymentJournalLineSet… False False 00005ea6-0000-0000-7804-005001000000\n", + "CustomerPaymentMethodEntity False False 00005ea6-0000-0000-d705-005001000000\n", + "CustomerPaymentMethodSpecific… False False 00005ea6-0000-0000-3907-005001000000\n", + "CustomerPostalAddressEntity False False 00005ea6-0000-0000-0006-005001000000\n", + "CustomerPriorityClassificatio… False False 00005ea6-0000-0000-9e06-005001000000\n", + "CustomOfficesEntity False False 00005ea6-0000-0000-7608-005001000000\n", + "CustPaymentInterestCodeEntity False False 00005ea6-0000-0000-8008-005001000000\n", + "CustTableChangeProposalFieldE… False False 00005ea6-0000-0000-8d02-005001000000\n", + "CustTradingPartnerCodeEntity False False 00005ea6-0000-0000-7b08-005001000000\n", + "CustWriteOffFinancialReasonsS… False False 00005ea6-0000-0000-1f04-005001000000\n", + "DimAttributeCustGroupEntity False False 00005ea6-0000-0000-0708-005001000000\n", + "DimAttributeCustTableEntity False False 00005ea6-0000-0000-e502-005001000000\n", + "DualWriteCustomerActivitiesEn… False False 00005ea6-0000-0000-1806-005001000000\n", + "DualWriteCustomerActivitiesV2… False False 00005ea6-0000-0000-b117-005001000000\n", + "EngChgEngineeringChangeCustom… False False 00005ea6-0000-0000-9209-005001000000\n", + "FinTagCustomListValueEntity False False 00005ea6-0000-0000-e31a-005001000000\n", + "FiscalDocumentTypesForCustome… False False 00005ea6-0000-0000-9b03-005001000000\n", + "GUPCustomerPricingAttributeGr… False False 00005ea6-0000-0000-d518-005001000000\n", + "GUPCustomerPricingAttributeLi… False False 00005ea6-0000-0000-ee18-005001000000\n", + "InventInventoryProfileCustome… False False 00005ea6-0000-0000-de05-005001000000\n", + "InventSupplementaryProductCus… False False 00005ea6-0000-0000-8117-005001000000\n", + "ITMCustomsDescriptionEntity False False 00005ea6-0000-0000-af0a-005001000000\n", + "LTMCustDefaultDocumentClassEn… False False 00005ea6-0000-0000-de1a-005001000000\n", + "LTMCustInvoiceJourEntity False False 00005ea6-0000-0000-dd1a-005001000000\n", + "LTMCustInvoiceTableEntity False False 00005ea6-0000-0000-be1a-005001000000\n", + "LTMCustTableEntity False False 00005ea6-0000-0000-b51a-005001000000\n", + "LTMCustTaxesAreaDetailEntity False False 00005ea6-0000-0000-d31a-005001000000\n", + "LTMCustTaxesAreaEntity False False 00005ea6-0000-0000-b21a-005001000000\n", + "LTMCustVendDocumentClassEntity False False 00005ea6-0000-0000-ba1a-005001000000\n", + "MarkupPeriodChargeRuleCustome… False False 00005ea6-0000-0000-9017-005001000000\n", + "MarkupPeriodChargeRuleLineCus… False False 00005ea6-0000-0000-8a17-005001000000\n", + "MCRContinuityCustomerPaymentE… False False 00005ea6-0000-0000-3f04-005001000000\n", + "MCRCouponCustomerEntity False False 00005ea6-0000-0000-1108-005001000000\n", + "MCRCustomerCreditPaymentEntity False False 00005ea6-0000-0000-7805-005001000000\n", + "MCRCustomerInvoicePaymentEnti… False False 00005ea6-0000-0000-2602-005001000000\n", + "MCRInstallmentPlanCustomerPay… False False 00005ea6-0000-0000-0a05-005001000000\n", + "MCRPostageDiscountCustomerGro… False False 00005ea6-0000-0000-5602-005001000000\n", + "MCRSalesOrderCustomerPaymentE… False False 00005ea6-0000-0000-bb02-005001000000\n", + "PriceDiscLineDiscountCustomer… False False 00005ea6-0000-0000-2302-005001000000\n", + "PriceDiscMultilineDiscountCus… False False 00005ea6-0000-0000-aa03-005001000000\n", + "PriceDiscPriceCustomerGroupEn… False False 00005ea6-0000-0000-8504-005001000000\n", + "PriceDiscTotalDiscountCustome… False False 00005ea6-0000-0000-6304-005001000000\n", + "ProjCustomerRetentionTermEnti… False False 00005ea6-0000-0000-3b07-005001000000\n", + "ProjCustomerRetentionTermSche… False False 00005ea6-0000-0000-7c04-005001000000\n", + "ProjProjectGrantCustomerTypeE… False False 00005ea6-0000-0000-2807-005001000000\n", + "RetailClientBookCustomerEntity False False 00005ea6-0000-0000-dd03-005001000000\n", + "RetailCustomerAffiliationEnti… False False 00005ea6-0000-0000-5f03-005001000000\n", + "RetailCustomerSearchFieldEnti… False False 00005ea6-0000-0000-ea0a-005001000000\n", + "RetailCustomerSearchFieldTran… False False 00005ea6-0000-0000-f00a-005001000000\n", + "RetailDesignerCustomerAttribu… False False 00005ea6-0000-0000-4a07-005001000000\n", + "RetailExternalIdToCustomerMap… False False 00005ea6-0000-0000-c304-005001000000\n", + "RetailTerminalCustomFieldEnti… False False 00005ea6-0000-0000-e50a-005001000000\n", + "RetailTmpCustomerMediaEntity False False 00005ea6-0000-0000-7a05-005001000000\n", + "RetailTransactionFiscalCustom… False False 00005ea6-0000-0000-2202-005001000000\n", + "SalesCustomerProductDescripti… False False 00005ea6-0000-0000-1802-005001000000\n", + "SalesCustomerProductDescripti… False False 00005ea6-0000-0000-a603-005001000000\n", + "SalesExternallyMaintainedCust… False False 00005ea6-0000-0000-a007-005001000000\n", + "SalesExternallyMaintainedCust… False False 00005ea6-0000-0000-4f04-005001000000\n", + "SalesExternallyMaintainedCust… False False 00005ea6-0000-0000-6405-005001000000\n", + "smmCustomerContactPersonCDSEn… False False 00005ea6-0000-0000-7402-005001000000\n", + "SubBillCustomerSplitLineEntity False False 00005ea6-0000-0000-3617-005001000000\n", + "SubBillCustomerSplitTableEnti… False False 00005ea6-0000-0000-3417-005001000000\n", + "SysCustomFieldPicklistValueEn… False False 00005ea6-0000-0000-9f09-005001000000\n", + "TAMRebateCustomerRebateAndDed… False False 00005ea6-0000-0000-b609-005001000000\n", + "TAMRebateCustomerRebateAndDed… False False 00005ea6-0000-0000-b509-005001000000\n", + "TAMRebateCustomerRebateReduct… False False 00005ea6-0000-0000-bc09-005001000000\n", + "TaxCustomsBillOfEntryNumberEn… False False 00005ea6-0000-0000-5707-005001000000\n", + "TaxCustomsImportInvoiceNumber… False False 00005ea6-0000-0000-8205-005001000000\n", + "TaxCustomsShippingBillNumberE… False False 00005ea6-0000-0000-d903-005001000000\n", + "TaxCustomsTariffCodeEntity False False 00005ea6-0000-0000-1207-005001000000\n", + "TaxCustomsTaxRegistrationNumb… False False 00005ea6-0000-0000-9202-005001000000\n", + "TaxServiceTaxRegistrationNumb… False False 00005ea6-0000-0000-9b1a-005001000000\n", + "TNVEDToCustomsPaymentsRelatio… False False 00005ea6-0000-0000-0103-005001000000\n", + "\n" + ] + } + ], + "source": [ + "$fnoEnvironments = (Get-BapEnvironment | Where-Object { $_.LinkedappLcsEnvUri -ne $null })\n", + "Get-BapEnvironmentVirtualEntity -EnvironmentId $fnoEnvironments[0].Id -Name Cust*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Compare cmdlets\n", + "\n", + "The `Compare` cmdlets are used to compare different aspects of your business applications. They are useful to understand the differences between environments, solutions, and other objects. Internally, they use the `Get` cmdlets to retrieve the information to compare.\n", + "\n", + "## Compare-BapEnvironmentUser\n", + "\n", + "The `Compare-BapEnvironmentUser` cmdlet compares the users of two environments. It is useful to understand the differences between the users of a development and a production environment, for example. Usually, you run the cmdlet with the `-ShowDiffOnly` switch to get only the users that are different between the two environments." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[32;1mEmail \u001b[0m\u001b[32;1m Name \u001b[0m\u001b[32;1m AppId \u001b[0m\u001b[32;1m SourceId\u001b[0m\n", + "\u001b[32;1m----- \u001b[0m \u001b[32;1m---- \u001b[0m \u001b[32;1m----- \u001b[0m \u001b[32;1m-------- \u001b[0m\n", + "user1@mycompany.com User First abcdefgh-1234-…\n", + "user2@mycompany.com User Second abcdefgh-1234-…\n", + "\n" + ] + } + ], + "source": [ + "Compare-BapEnvironmentUser -SourceEnvironmentId $environments[0].Id -DestinationEnvironmentId $environments[1].Id -ShowDiffOnly" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compare-BapEnvironmentD365App\n", + "\n", + "The `Compare-BapEnvironmentD365App` cmdlet compares the solutions of two environments. It is useful to understand the differences between the solutions of a development and a production environment, for example. Again, you usually run the cmdlet with the `-ShowDiffOnly` switch to get only the solutions that are different between the two environments." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[32;1mPackageId \u001b[0m\u001b[32;1m PackageName \u001b[0m\u001b[32;1m SourceVersion \u001b[0m\u001b[32;1m Destination\u001b[0m\n", + "\u001b[32;1m \u001b[0m\u001b[32;1m \u001b[0m\u001b[32;1m \u001b[0m\u001b[32;1m Version\u001b[0m\n", + "\u001b[32;1m--------- \u001b[0m \u001b[32;1m----------- \u001b[0m \u001b[32;1m------------- \u001b[0m \u001b[32;1m-----------\u001b[0m\n", + "11111111-1111-1111-1111-111111111111 AgentProductivityToolsAnchor 9.2.24023.2000 Missing \n", + "22222222-2222-2222-2222-222222222222 msdyn_AppProfileManagerAnchor 10.1.24021.1005 Missing \n", + "33333333-3333-3333-3333-333333333333 msdynce_CustomerCareIntellige… 0.0.0.0 Missing \n", + "\n" + ] + } + ], + "source": [ + "Compare-BapEnvironmentD365App -SourceEnvironmentId $environments[0].Id -DestinationEnvironmentId $environments[1].Id -ShowDiffOnly" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Other cmdlets\n", + "\n", + "The `Get` and `Compare` cmdlets are just a few of the cmdlets available in the module. In later, more scenario-specific notebooks, you will learn more about other cmdlets. But for now, you can use the `Get-Command` and `Get-Help` cmdlets to learn about them. Feel free to use the following cell for your own experiments." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "dotnet_interactive": { + "language": "pwsh" + }, + "polyglot_notebook": { + "kernelName": "pwsh" + }, + "vscode": { + "languageId": "polyglot-notebook" + } + }, + "outputs": [], + "source": [ + "# How about listing all the environments that have a specific D365 app installed?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".NET (C#)", + "language": "C#", + "name": ".net-csharp" + }, + "language_info": { + "name": "csharp" + }, + "polyglot_notebook": { + "kernelInfo": { + "defaultKernelName": "csharp", + "items": [ + { + "aliases": [], + "name": "csharp" + } + ] + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}