Skip to content

Microsoft Azure PowerShell Developer Guide

Mark Cowlishaw edited this page Mar 23, 2016 · 32 revisions

##Get it! A normal git geek can clone the repo.

Configuring Visual Studio

Development Environment

  • You need to have this environment variable setup EnableNuGetPackageRestore=true
  • Use Visual Studio 2013 (never use 2012 as the build may fail)

Prerequisites

Change PowerShell execution policy

Change machine's PowerShell execution policy to Unrestricted (see this to know how to change it). Make sure that this change is applied to PowerShell.exe under System32 and SysWOW64:

  • C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
  • C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe

Install Prerequisites

Option #1, Automatically install prerequisites by executing tools\Build.ps1

Option #2, or do it manually by installing these:

  • Install Wix 3.8
  • Make sure that git.exe and wix bin folder are in you PATH environment variable.
  • Set environment variable EnableNuGetPackageRestore to value true

Build the solution

  • msbuild build.proj

Run unit tests

  • Read the next section to setup environment variables required to run scenario tests.
  • Execute tools\RunCheckInTests.ps1

Preliminary Steps for adding new project (Service Manager or Resource Manager):

Adding a new project

  • Add the Service Management or Resource Management project
  • The new code should depend on the Commands.Common* projects only and if there are other external dependencies coming from a project in old code please contact us back to sort this out.
  • The project should not have a direct dependency on the storage SDK and should rely on Commands.Common.Storage
  • The cmdlets code must follow the convenience client convention (check Test-AzureResourceGroupTemplate cmdlet src code) meaning that the cmdlet src code file should minimum required business logic and just call the convenience client.
  • The scenario tests should be added in Commands.ScenarioTest and follow same convention that Resources have.
  • No need to have C# unit tests unless there is a need for deep testing a specific method in a class.
  • Upon submitting the PR
    • Make sure to record mocks for the scenario tests and check them in as well.
    • Update the installer to include your files see instructions.
    • Include the dll help file. CmdLet Help Doc
    • Update Changelog.txt in the root to include the change that was made

Adding new Service Management (RDFE) project

When adding a new Service Management project please follow these guidelines:

  • Add new folder under ServiceManagement that have your service specific name. Use the same name that would be visible on the portal and do not use generic names.
  • Under the newly created folder create the corresponding cmdlets and test project following the same naming convention in ManagedCache folder projects.

Adding new Resource Manager (CSM) project

When adding a new Resource Manager project please follow these guidelines:

  • Add new folder under ResourceManager that have your service specific name. Use the same name that would be visible on the portal and do not use generic names.
  • Under the newly created folder create the corresponding cmdlets and test project following the same naming convention in Resource folder projects.

Updating the installer

  • The installer should be updated whenever new files are added or file paths are changed.
  • To regenerate the installer wxi file, follow these steps:
    • Ensure that the Wix tools bin folder (and heat.exe) are on the path
    • set the environment variable AzurePSRoot to the path to your local clone of azure-powershell:
> set AzurePSRoot=c:\repo\azure-powershell
  • build the cmdlets.
> msbuild build.proj

This builds both the cmdlets and the installer - if your changes have removed or renamed files previously contained in the installer, the installer build may fail. In this case, you can ignore installer build failures.

  • generate the new wxi file using the generate.ps1 powershell script
> powershell .\tools\installer\generate.ps1 <BUILDCONFIG>

where BuildConfig is DEBUG or RELEASE, depending on which build configuration you are using.

  • verify that the changes look correct using git diff. Often unintended changes will happen if your repository is not clean. If this occurs, revert the wxi file change, commit all other changes and use git clean -xdf to wipe out all untracked filesfrom your git clone

Setup for scenario tests

Using AD (works for Resource Manager and Service Manager)
  • Set TEST_CSM_ORGID_AUTHENTICATION environment variable for Resource Manager testing
  • Set TEST_ORGID_AUTHENTICATION environment variable for Service Manager testing
  • Both environment variables need to be set for the test framework to have success
  • Execute tools\SetupHttpRecordEnv.ps1 to know how the environment variable contents are expected to be.
Using Certificate (works for Service Manager only)
  • In a Azure Storage, create a new container called testcredentials-production
  • upload these three files there:
    • default.publishsettings: that's publish settings file used in the tests
    • environment.yml: these are environment variables you want to inject (check yml file format)
    • variables.yml: these are PowerShell variables you want to inject
  • On your machine set these environment variables:
    • AZURE_TEST_ENVIRONMENT=production
    • AZURE_STORAGE_ACCOUNT=<storage account name>
    • AZURE_STORAGE_ACCESS_KEY=<storage account key>
  • Run scenario tests by executing this cmd: msbuild build.proj /t:Scenariotest

AD Scenario Tests

Create these environment variables for the AD scenario tests:

  • AZURE_LIVEID should be a UserId and Password for a valid liveid account. AZURE_LIVEID=UserId=<user@hotmail.com>;Password=<Password>
  • AZURE_ORGID_FPO should be an orgid and password for an account that does not have any subscriptions or role assignments associated with it. It is supposed to be a foreign principal in your current tenant. AZURE_ORGID_FPO=UserId=<user@orgid.com>;Password=<Password>
  • AZURE_ORGID_ONE_TENANT_ONE_SUBSCRIPTION should be an account that is in a single tenant and has access to the subscription managed by that AD tenant. AZURE_ORGID_ONE_TENANT_ONE_SUBSCRIPTION=UserId=<user@orgid.com>;Password=<Password>;SubscriptionId=<SubscriptionId>;AADAuthEndpoint=https://login.windows.net/
  • AZURE_SERVICE_PRINCPAL should be a service principal – an application defined in the subscription’s tenant – that has management access to the subscription (or at least to a resource group in the tenant). AZURE_SERVICE_PRINCIPAL=UserId=<UserGuid>;Password=<password>;AADTenant=<TenantGuid>;SubscriptionId=<SubscriptionId>

Recording/Running tests

  • Execute tools\SetupHttpRecordEnv.ps1
  • Run the test and make sure that you got a generated *.json file that matches the test name in the bin folder under SessionRecords folder
  • Copy SessionRecords folder inside the test project and add all *.json files in Visual Studio setting "Copy to Output Directory" property to "Copy if newer"
  • To assure that the records work fine, change AZURE_TEST_MODE to Playback and run the test which should be run mocked not live.

Enable running Windows PowerShell when debugging

  • Choose any project and set it as startup project.
  • Open this project properties.
  • Go to Debug tab.
  • Under Start Action pick Start external program and type Windows PowerShell directory (i.e. C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe)

Importing modules automatically when start debugging

  • Assuming the Debug page is opened, go to Start Options
  • In Command line arguments type this command
-NoExit -Command "Import-Module <Modules to import>"
  • For example this command loads module manifest:
-NoExit -Command "Import-Module <Path to Azure.psd1>\Azure.psd1"

It's recommended to enable verbose as it's enabled by default when using Azure PowerShell shortcut

-NoExit -Command "Import-Module <Path to Azure.psd1>\Azure.psd1;$VerbosePreference='Continue'"

Adding new scenario test

Configuring scenario test code

  • Create a new class in either Commands.ScenarioTests or Commands.XXXX.Test (where XXX is the name of the resource provider)
    • If setting up a new project add reference to Commands.ScenarioTests.Common
    • If setting up a new project set post build event to xcopy "$(SolutionDir)Package\$(ConfigurationName)\*.*" $(TargetDir) /Y /E
    • Add reference to any project that will be tested
  • Create a ps1 file in the same folder that contains actual tests (see sample)
    • Use Assert-AreEqual x y to verify that values are the same
    • Use Assert-AreNotEqual x y to verify that values are not the same
    • Use Assert-Throws scriptblock message to verify exception is being thrown
    • Use Assert-ThrowsContains scriptblock substring to verify exception is being thrown and contains a substring
    • Use Assert-Env env[] to verify environment variables
    • Use Assert-True scriptblock to verify that a script block returns true
    • Use Assert-False scriptblock to verify that a script block returns false
    • Use Assert-Null object to verify that the object is null
    • Use Assert-NotNull object to verify that the object is not null
    • Use Assert-Exists path to verify that the file exists
    • Use Assert-AreEqualArray a1 a2 to compare arrays
  • Add a private variable EnvironmentSetupHelper helper to the test class
  • Set each test as an XUnit test that does the following:
              // Enable undo functionality as well as mock recording
              using (UndoContext context = UndoContext.Current)
              {
                  // Configure recordings
                  context.Start(TestUtilities.GetCallingClass(), TestUtilities.GetCurrentMethodName());
    
                  // See explanation below
                  SetupManagementClients();
    
                  // Specify either ResourceManager or ServiceManagement mode
                  helper.SetupEnvironment(AzureModule.AzureResourceManager);
    
                  // Add all ps1 files used in the test
                  helper.SetupModules(AzureModule.AzureResourceManager, "ScenarioTests\\Common.ps1",
                      "ScenarioTests\\" + this.GetType().Name + ".ps1");
    
                  // Run actual test
                  helper.RunPowerShellTest(scripts);
              }
  • Add SetupManagementClients() method that creates instances of clients used in the actual test and adds them to the helper
      // Create clients via TestBase.GetServiceClient to setup mocking
      var client1 = TestBase.GetServiceClient<ResourceManagementClient>(new CSMTestEnvironmentFactory());
      var client2 = TestBase.GetServiceClient<XXXX>(new RDFETestEnvironmentFactory());
    
      helper.SetupManagementClients(client1, client2);
      // Alternatively call helper.SetupSomeOfManagementClients(...) if not all clients can be pre-initialized
      // Any client not passed in to this method will not be recorded

Misc

Understanding piping

In Powershell cmdlets pipe objects between one another. Cmdlets should not return text, they should return objects.

For example, if you use our Azure cmdlets, you can do the following

Get-AzureWebsite | Remove-AzureWebsite

Get-AzureWebsite will return a set of Website objects and then invoke Remove-AzureWebsite passing each object one by one.

Piping in

As long as the property names of the objects getting returned from Get-AzureWebsite match those of Remove-AzureWebsite, and Remove-AzureWebsite's properties are properly annotated with [Parameter(ValueFromPipeline)], then piping between them works.

In this case, the RemoveAzureWebsite and GetAzureWebsite classes both inherit from WebsitesBaseCmdlet. This base class includes a "Name" parameter which is properly annotated as is shown below:

[Parameter(Position = 0, Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "The web site name.")]
[ValidateNotNullOrEmpty]
public string Name
{
   get;
   set;
}

ValueFromPipelineByPropertyName=true instructs Powershell to try to match any objects coming in from the pipeline by the property name and type. If it finds a match then the value will be plugged in. Multiple properties can be annotated in this way.

Piping out

To pipe out, you use the cmdlet's WriteObject method. This will then write the objects out to the pipeline. Below is an example of how this is done in the GetAzureWebsite class.

protected virtual void WriteWebsites(IEnumerable<Site> websites)
{
    WriteObject(websites, true);
}

For more information on piping, see the following links:

Getting a MAML generated lib used in Azure PowerShell

  • Create/Update specs project in the specs repo and send pull request
  • Wait until it gets merged and then publish it using this job. Note that you need to provide your package name
  • Update the corresponding MAML lib and send pull request to .NET SDK repo
  • Wait until it gets merged and then publish the MAML package using this job. Make sure that when you publish it's safe for other MAML libs
  • Now you can use the published lib in Azure PowerShell by consuming it as NuGet package

Publish to PowerShell gallery