From faaf864ef259725fe35316ebd2f9343869a00c8c Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:33:36 +0000 Subject: [PATCH 01/18] Add learn commands notebook --- learning/notebooks/get-started.ipynb | 11 + learning/notebooks/learn-commands.ipynb | 616 ++++++++++++++++++++++++ 2 files changed, 627 insertions(+) create mode 100644 learning/notebooks/learn-commands.ipynb 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/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 +} From de2909ee86baaf0a27e757eb8dc21817d4a8d8fa Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Mon, 4 Mar 2024 17:40:54 +0000 Subject: [PATCH 02/18] Add confirm integration notebook --- learning/notebooks/confirm-integration.ipynb | 111 +++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 learning/notebooks/confirm-integration.ipynb 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 +} From 46fa24f23c024e18ede600cdaebf59bd1dc2c9b7 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:07:41 +0000 Subject: [PATCH 03/18] Add app updates installation notebook --- learning/notebooks/install-appupdates.ipynb | 139 ++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 learning/notebooks/install-appupdates.ipynb 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 +} From 422d996de1947f082ea82829827801b4e2205fce Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:18:41 +0000 Subject: [PATCH 04/18] :memo: Add notebooks documentation --- learning/notebooks/ReadMe.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 learning/notebooks/ReadMe.md 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. From 7474c56fcd961e99b3b757e8886c810fac5f2699 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:14:47 +0100 Subject: [PATCH 05/18] add reusable workflow for build and release --- .github/workflows/tmpl-build-release.yml | 41 ++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/tmpl-build-release.yml diff --git a/.github/workflows/tmpl-build-release.yml b/.github/workflows/tmpl-build-release.yml new file mode 100644 index 0000000..9e3da86 --- /dev/null +++ b/.github/workflows/tmpl-build-release.yml @@ -0,0 +1,41 @@ +# 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: boolean + skippublish: + description: "Determines if the publishing to the PowerShell Gallery is skipped" + default: false + required: false + type: boolean + 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 \ No newline at end of file From bff7e8e64ce41fcd677be2ec976016150d0093f8 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:15:18 +0100 Subject: [PATCH 06/18] add worklow with manual trigger to test reusable workflow for build and release --- .github/workflows/build-manual.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/build-manual.yml diff --git a/.github/workflows/build-manual.yml b/.github/workflows/build-manual.yml new file mode 100644 index 0000000..e60454c --- /dev/null +++ b/.github/workflows/build-manual.yml @@ -0,0 +1,17 @@ +on: + workflow_dispatch: + inputs: + skippublish: + description: "Determines if the publishing to the PowerShell Gallery is skipped" + default: false + required: false + type: boolean + +jobs: + call-tmpl-build-release: + uses: ./.github/workflow/tmpl-build-release.yml + with: + autoversion: true + skippublish: ${{ inputs.skippublish }} + secrets: + apikey: $env:APIKEY \ No newline at end of file From 3d76ccbec9b9667b492521d9a6ebde1e56698ae7 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:21:33 +0100 Subject: [PATCH 07/18] fix call of reusable workflow --- .github/workflows/build-manual.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-manual.yml b/.github/workflows/build-manual.yml index e60454c..22d78a4 100644 --- a/.github/workflows/build-manual.yml +++ b/.github/workflows/build-manual.yml @@ -9,9 +9,9 @@ on: jobs: call-tmpl-build-release: - uses: ./.github/workflow/tmpl-build-release.yml - with: - autoversion: true - skippublish: ${{ inputs.skippublish }} - secrets: - apikey: $env:APIKEY \ No newline at end of file + uses: ./.github/workflows/tmpl-build-release.yml + with: + autoversion: true + skippublish: ${{ inputs.skippublish }} + secrets: + apikey: $env:APIKEY \ No newline at end of file From bb69fa1f69722677868d97a286ea65833617dab6 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:47:19 +0100 Subject: [PATCH 08/18] fix boolean values for PowerShell switch parameters --- .github/workflows/build-manual.yml | 7 +++++-- .github/workflows/tmpl-build-release.yml | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-manual.yml b/.github/workflows/build-manual.yml index 22d78a4..5f0a9dd 100644 --- a/.github/workflows/build-manual.yml +++ b/.github/workflows/build-manual.yml @@ -3,9 +3,12 @@ on: inputs: skippublish: description: "Determines if the publishing to the PowerShell Gallery is skipped" - default: false + default: $True required: false - type: boolean + type: choice + options: + - $False + - $True jobs: call-tmpl-build-release: diff --git a/.github/workflows/tmpl-build-release.yml b/.github/workflows/tmpl-build-release.yml index 9e3da86..df315f8 100644 --- a/.github/workflows/tmpl-build-release.yml +++ b/.github/workflows/tmpl-build-release.yml @@ -10,14 +10,14 @@ on: inputs: autoversion: description: "Determines if the module's build version number is automatically incremented" - default: true + default: $True required: false - type: boolean + type: string skippublish: description: "Determines if the publishing to the PowerShell Gallery is skipped" - default: false + default: $False required: false - type: boolean + type: string secrets: apikey: description: "Key for the PowerShell Gallery API" From 2c548c33df1db10c454f6201a436057198bb4365 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:55:23 +0100 Subject: [PATCH 09/18] remove unnecessary autoversion input --- .github/workflows/build-manual.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-manual.yml b/.github/workflows/build-manual.yml index 5f0a9dd..f063e22 100644 --- a/.github/workflows/build-manual.yml +++ b/.github/workflows/build-manual.yml @@ -14,7 +14,6 @@ jobs: call-tmpl-build-release: uses: ./.github/workflows/tmpl-build-release.yml with: - autoversion: true skippublish: ${{ inputs.skippublish }} secrets: apikey: $env:APIKEY \ No newline at end of file From 8e0bf225ba3056fddf133baff90ca42b583cb626 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:12:35 +0100 Subject: [PATCH 10/18] add GitHub release creation --- .github/workflows/build-manual.yml | 9 +++++++++ .github/workflows/tmpl-build-release.yml | 24 +++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-manual.yml b/.github/workflows/build-manual.yml index f063e22..c48bc74 100644 --- a/.github/workflows/build-manual.yml +++ b/.github/workflows/build-manual.yml @@ -9,11 +9,20 @@ on: 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: $env:APIKEY \ No newline at end of file diff --git a/.github/workflows/tmpl-build-release.yml b/.github/workflows/tmpl-build-release.yml index df315f8..2d130cd 100644 --- a/.github/workflows/tmpl-build-release.yml +++ b/.github/workflows/tmpl-build-release.yml @@ -18,6 +18,11 @@ on: 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" @@ -38,4 +43,21 @@ jobs: shell: powershell - name: Build run: .\build\vsts-build.ps1 -ApiKey ${{ secrets.apikey }} -AutoVersion:${{ inputs.autoversion }} -SkipPublish:${{ inputs.skippublish }} - shell: powershell \ No newline at end of file + shell: powershell + + - name: Get version + run: | + $publishDir = Get-Item -Path $WorkingDirectory -Name publish -ItemType Directory + [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 From c878f680fd700e57337c3479db90bcc2618b2afe Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:27:05 +0100 Subject: [PATCH 11/18] fix working directory --- .github/workflows/tmpl-build-release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tmpl-build-release.yml b/.github/workflows/tmpl-build-release.yml index 2d130cd..6c5ec4f 100644 --- a/.github/workflows/tmpl-build-release.yml +++ b/.github/workflows/tmpl-build-release.yml @@ -47,7 +47,8 @@ jobs: - name: Get version run: | - $publishDir = Get-Item -Path $WorkingDirectory -Name publish -ItemType Directory + tree + $publishDir = Get-Item -Path $env:GITHUB_WORKSPACE -Name publish -ItemType Directory [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 From 918251399ce8b087b84d560e01af594848b51222 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:34:48 +0100 Subject: [PATCH 12/18] fix invalid -Name parameter for Get-Item --- .github/workflows/tmpl-build-release.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tmpl-build-release.yml b/.github/workflows/tmpl-build-release.yml index 6c5ec4f..d5e11ec 100644 --- a/.github/workflows/tmpl-build-release.yml +++ b/.github/workflows/tmpl-build-release.yml @@ -47,8 +47,7 @@ jobs: - name: Get version run: | - tree - $publishDir = Get-Item -Path $env:GITHUB_WORKSPACE -Name publish -ItemType Directory + $publishDir = Get-Item -Path publish -ItemType Directory [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 From 5cbe4adaa5b94f24de4728150238a5aacaa975b0 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:41:41 +0100 Subject: [PATCH 13/18] remove invalid -ItemType parameter from get-item --- .github/workflows/tmpl-build-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tmpl-build-release.yml b/.github/workflows/tmpl-build-release.yml index d5e11ec..336c18a 100644 --- a/.github/workflows/tmpl-build-release.yml +++ b/.github/workflows/tmpl-build-release.yml @@ -47,7 +47,7 @@ jobs: - name: Get version run: | - $publishDir = Get-Item -Path publish -ItemType Directory + $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 From 3ea2f23ec9b04469517be5c29b47ab96394851a0 Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:52:07 +0100 Subject: [PATCH 14/18] call build and release template --- .github/workflows/build.yml | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 012bfe7..45efd09 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: $env:APIKEY \ No newline at end of file From 0ab1b0fc6b3dee876bd10b7049e9b20373aefd9b Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:58:54 +0100 Subject: [PATCH 15/18] fix api key secret reference --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 45efd09..7b73477 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,4 +11,4 @@ jobs: skippublish: $False skipghrelease: $False secrets: - apikey: $env:APIKEY \ No newline at end of file + apikey: ${{ secrets.ApiKey }} \ No newline at end of file From c5408699e66846e0b535d20b0333cfb25802592a Mon Sep 17 00:00:00 2001 From: Florian Hopfner <33372796+FH-Inway@users.noreply.github.com> Date: Sat, 9 Mar 2024 19:32:57 +0100 Subject: [PATCH 16/18] fix api key secret reference in manual build action --- .github/workflows/build-manual.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-manual.yml b/.github/workflows/build-manual.yml index c48bc74..c9c09c9 100644 --- a/.github/workflows/build-manual.yml +++ b/.github/workflows/build-manual.yml @@ -25,4 +25,4 @@ jobs: skippublish: ${{ inputs.skippublish }} skipghrelease: ${{ inputs.skipghrelease }} secrets: - apikey: $env:APIKEY \ No newline at end of file + apikey: ${{ secrets.ApiKey }} \ No newline at end of file From 381f2248c578502a0d0a174bd4cb896ff8bd6a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=B6tz=20Jensen?= Date: Mon, 8 Apr 2024 16:15:56 +0200 Subject: [PATCH 17/18] Feature: Implement Export-BapEnvironmentSolution --- d365bap.tools/d365bap.tools.psd1 | 4 + .../Export-BapEnvironmentSolution.ps1 | 113 +++++++++++ .../functions/Get-BapEnvironmentSolution.ps1 | 159 ++++++++++++++++ .../Export-BapEnvironmentSolution.Tests.ps1 | 62 ++++++ .../Get-BapEnvironmentSolution.Tests.ps1 | 75 ++++++++ d365bap.tools/xml/d365bap.tools.Format.ps1xml | 46 +++++ docs/Export-BapEnvironmentSolution.md | 96 ++++++++++ docs/Get-BapEnvironmentSolution.md | 177 ++++++++++++++++++ 8 files changed, 732 insertions(+) create mode 100644 d365bap.tools/functions/Export-BapEnvironmentSolution.ps1 create mode 100644 d365bap.tools/functions/Get-BapEnvironmentSolution.ps1 create mode 100644 d365bap.tools/tests/functions/Export-BapEnvironmentSolution.Tests.ps1 create mode 100644 d365bap.tools/tests/functions/Get-BapEnvironmentSolution.Tests.ps1 create mode 100644 docs/Export-BapEnvironmentSolution.md create mode 100644 docs/Get-BapEnvironmentSolution.md 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..d5389c8 --- /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-Host "$($tmp[0])" -ForegroundColor Yellow + + $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..cb2c053 --- /dev/null +++ b/d365bap.tools/functions/Get-BapEnvironmentSolution.ps1 @@ -0,0 +1,159 @@ + +<# + .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()] + 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 From 6f59f5c2702bd0a63dfc25c97ba5847f2e795128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=B6tz=20Jensen?= Date: Mon, 8 Apr 2024 16:15:56 +0200 Subject: [PATCH 18/18] Feature: Implement Export-BapEnvironmentSolution --- d365bap.tools/d365bap.tools.psd1 | 4 + .../Export-BapEnvironmentSolution.ps1 | 113 +++++++++++ .../functions/Get-BapEnvironmentSolution.ps1 | 160 ++++++++++++++++ .../Export-BapEnvironmentSolution.Tests.ps1 | 62 ++++++ .../Get-BapEnvironmentSolution.Tests.ps1 | 75 ++++++++ d365bap.tools/xml/d365bap.tools.Format.ps1xml | 46 +++++ docs/Export-BapEnvironmentSolution.md | 96 ++++++++++ docs/Get-BapEnvironmentSolution.md | 177 ++++++++++++++++++ 8 files changed, 733 insertions(+) create mode 100644 d365bap.tools/functions/Export-BapEnvironmentSolution.ps1 create mode 100644 d365bap.tools/functions/Get-BapEnvironmentSolution.ps1 create mode 100644 d365bap.tools/tests/functions/Export-BapEnvironmentSolution.Tests.ps1 create mode 100644 d365bap.tools/tests/functions/Get-BapEnvironmentSolution.Tests.ps1 create mode 100644 docs/Export-BapEnvironmentSolution.md create mode 100644 docs/Get-BapEnvironmentSolution.md 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