STM32 Adventures – Communication using I2C

When working with STM32 using registries only it is important that you have REFERENCE MANUAL and DATASHEET available. We will use them a lot 🙂


Today we start with I2C communication protocol. Configuration of it might be a bit of tricky at the beginning gut I hope to explain to you what I have came across of when trying to get the protocol to work. For more details about I2C itself check out wiki .

Device address

Since I have worked with it I usually like to start off with defining address of the device that I’m working with ( or devices if you work with more than one ).

#define DEVICE_ADDRESS 0x01

This has saved me many times from problems with wrong addressing and spending time troubleshooting something which is easy as above 🙂

Transmission flow

So now its time to dive back into RM. We need to find part about I2C. Since we will be building a master device we focus there (at least for now ). As you can see on image below its shows really nice what we must program. Specific events at specific point of transmissions ( for transmitter and receiver ). This makes it really easy to code against.



Configuration of peripherial

Let’s move on to configuration. What is important for you is to know that the code presented here is not using interrupts or DMA. Reason for this is slow build up of skills. I will document those later on and update the links in this post accordingly.

Let’s start by finding which PINs we need to configure. For that one I jump to DS and search for I2C1 (that one I want to use ) and checkout Alternate Function Mappings



So now I can choose even what suits me more 🙂 I will stick with PB6 and PB7. Below function will set those pins as alternate function, high speed , open drain and finally configure that we use AF4 for those pins.

 * Function responsbile for configuration
 * of GPIO pins
void i2c_setup_gpio(void)
			GPIO_MODER_MODER7_1; 	// AF: PB7 => I2C1_SDA




	 * Alternate functions are configured in ARFL for PINS 0..7
	 * and in ARFH for PINS 8..15
	 * Based on DS we will select appropiate AF0..AF7
	GPIOB->AFR[0] |= ( 1 << 30 ) | ( 1 << 26); // P6/P7 => AF4

If you have by any chance forgotten about RCC here is my routine which I used to enable all peripherals

void rcc_init(void)

			RCC_AHB1ENR_GPIOCEN;	// Enable clock: GPIOC
	RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;	// Enable clock: I2C1

	__DSB();	// Data Synchronization Barrier


Once hardware is ready we can configure the I2C registries.

void i2c_init(void)

	 *  Reset I2C from lock state
	//I2C1->CR1 |= I2C_CR1_SWRST;

	 * FREQ: Set frequencey based on APB1 clock
	 * 8MHz
	I2C1->CR2 &= ~(I2C_CR2_FREQ);
	I2C1->CR2 |= 0x08;

	 * Depending on the frequency of said prescaler must be installed in
	 * accordance with the selected data rate.
	 * We choose the maximum, for standard mode - 100 kHz:
	 *  8MHz / 100 = 80 kHz;
	I2C1->CCR &= ~I2C_CCR_CCR;
	I2C1->CCR |= 80;

	 * clock period is equal to (1 / 8 MHz = 125 ns), therefore the maximum rise time:
	 * 1000 ns / 125 ns = 8 + 1 (plus one - a small margin) = 3
	I2C1->TRISE = 9;

	 * Enable perifpherial at the END
	I2C1->CR1 = I2C_CR1_ACK|
	I2C_CR1_PE;     // PE : Peripherial enable

Its quite easy here – we set freq of peripherial ( in my case it was 8Mhz ) then set clock rate ( which has simple formula ) and then we setup time rise. All of this is documented in RM and clearly explained.

The only thing that is left now is to read and write from the device.

Read using I2C

uint8_t i2c_read(uint8_t address, uint8_t registry)
	uint32_t timeout = TIMEOUT_MAX;

	while(I2C1->SR2 & I2C_SR2_BUSY);		// Wait for BUSY line
	I2C1->CR1 |= I2C_CR1_START;				// Generate START condition

	while (!(I2C1->SR1 & I2C_SR1_SB)); 		// Wait for EV5
	I2C1->DR = address<<1;					// Write device address (W)

	while (!(I2C1->SR1 & I2C_SR1_ADDR));	// Wait for EV6
    (void)I2C1->SR2;						// Read SR2

	while (!(I2C1->SR1 & I2C_SR1_TXE));		// Wait for EV8_1
	I2C1->DR = registry;

	I2C1->CR1 |= I2C_CR1_STOP;				// Generate STOP condition

	I2C1->CR1 |= I2C_CR1_START;				// Generate START condition

	while (!(I2C1->SR1 & I2C_SR1_SB)); 		// Wait for EV5
	I2C1->DR = (address << 1 ) | 1;			// Write device address (R)

	while (!(I2C1->SR1 & I2C_SR1_ADDR));	// Wait for EV6
    I2C1->CR1 &= ~I2C_CR1_ACK;              // No ACK
    (void)I2C1->SR2;						// Read SR2

	while (!(I2C1->SR1 & I2C_SR1_RXNE));	// Wait for EV7_1
    uint8_t value = (uint8_t)I2C1->DR;      // Read value

    I2C1->CR1 |= I2C_CR1_STOP;			    // Generate STOP condition

	return value;

Writing using I2C

void i2c_write(uint8_t address, uint8_t registry, uint8_t data) 

	I2C1->CR1 |= I2C_CR1_START;				// Generate START condition

	while (!(I2C1->SR1 & I2C_SR1_SB)); 		// Wait for EV5
	I2C1->DR = address<<1;					// Write device address (W)

	while (!(I2C1->SR1 & I2C_SR1_ADDR));	// Wait for EV6
    reg2 = I2C1->SR2;						// Read SR2

	while (!(I2C1->SR1 & I2C_SR1_TXE));		// Wait for EV8_1
	I2C1->DR = registry;					// Write registry address

	while (!(I2C1->SR1 & I2C_SR1_BTF));	    // Wait for BTF
	I2C1->DR = data;

	I2C1->CR1 |= I2C_CR1_STOP;			    // Generate STOP condition


Code presented above has still work in progress as I need to add error control and timeouts – otherwise if something would go wrong now it would block whole uC




Code for this exercise can be of course found at github https://github.com/RafPe/STM32F4DISCOVERY-I2C













STM32 Adventures – output clock to pin

When working with STM32 using registries only it is important that you have REFERENCE MANUAL and DATASHEET available. We will use them a lot 🙂


In today’s post we will be outputting SYSCLK to external pin of our STM32F407 discovery board. As mentioned we dive into our RM and find part responsible for RCC configurations. From there it is easy to see which bits we need to output SYSCLK to our pin.



As you can see on the above reset value shows that desired SYSCLK and default no prescaler will be selected. Then in this instance let’s find out on which pin we have to enable our alternate function.

To do this we need to find it in DS.



Great – it is default system function! So the only thing we should do is just set this PIN as output

int main(void)


	 *  Enable SYSCLK output to PC9


let’s see it if works by looking into our logic level analyzer



We will defenitely be able to use this functionality in interacting with external hardware – but thats for future posts


STM32 – minimal libraries project

Hey! So you most likely here because you would like to challenge yourself and try to program 32bit microcontrollers using minimal amount of libraries ( HAL/CMSIS ). On the internet you will find multiple discussions which approach to use – I will not be touching that – I guess everyone has his own way 😉

We will starting by shortly discussing environment used and then we will move on to configuring our project. I will be using for this STM324F407 discovery board – but with the right changes you will be able to adjust it to your own needs.

I will cover in this post:

  • Creating new project in Eclipse
  • Setting all libraries
  • Setting project & compilator settings

Before you start you should have


This project will use several files i.e. startup file / linker script and several header files.


Setting up project

Go ahead and create new ARM project. Here we will not be using any ‘Hello world’ or prepared examples – we start blank!



Here just go through the wizard and finish. When done we will move on getting extra files we need for our project.


Create folders inside the project folder so the structure will look as follow


Download firmware using STM32Cube

Now we need to start our STM32Cube to get all files we will need to move on with our project. Go to help -> install new libraries and then select the firmware for your cortex. For me it will be for STM32F4



Get following files into the project directories:

├── cmsis
│   ├── include
│   │   ├── cmsis_gcc.h
│   │   ├── core_cm0plus.h
│   │   ├── core_cm4.h ** THIS IS BASED ON YOUR CORTEX!! If you have different take different lib file !! **
│   │   ├── core_cmFunc.h
│   │   ├── core_cmInstr.h
│   │   └── core_cmSimd.h
│   └── src
├── include
├── src
│   └── main.c ( this is file created by you )
└── system
    ├── include
    │   ├── stm32f407xx.h
    │   ├── stm32f4xx.h
    │   └── system_stm32f4xx.h
    └── src
        ├── startup_stm32f407xx.s
        └── system_stm32f4xx.c


Modify project settings

Now since we have all the files we need your project should look similar to the following



We will now go and modify several project settings. Open project preferences and make required modifications

  • Change extension used by assembler source file to be *.s ( it’s project specific setting )stm32_minimallibs_06
  • Change to appropriate cortex family modelstm32_minimallibs_07
  • Modify preprocessor definitions to match your processor ( this settings in image below are for STM32F407VGx )stm32_minimallibs_08
  • Modify includes in Assembler and Compiler so it contains “${ProjDirPath}/cmsis/include” and “${ProjDirPath}/system/include
  • Under Linker -> General have the following ${ProjDirPath}/STM32F407VGTx_FLASH.ld ( if you are using different MCU then you will need to change this )
  • Modify paths which include source codestm32_minimallibs_09


Blink the diode

So now you are in position to just blink the diode using the following code in main.c

 * main.c
 *  Created on: 12 Nov 2016
 *      Author: rafpe
#include "stm32f4xx.h"
#include "stm32f407xx.h"

int main(void)
	volatile uint32_t delay;

	RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // enable the clock to GPIOD


	GPIOD->MODER = (1 << 26); // set pin 13 to be general purpose output

	while (1)
		for (delay = 1000000; delay; delay--)
			GPIOD->ODR ^= (1 << 13);


Compile and you are ready to flash your device.




Given all setup here we are HSI is used as system clock source. We will definitely come back to this as we will need more than i.e. 16 Mhz! Happy coding!






Using STM32CubeMX on MacOS

When it comes to using multiple tools to program/develop it can be sometimes challenging to have them running in single OS. Might be that you would end up having virtual machine running some of your tools – which of course – does not make it easier at all!

One of tools that I really have need of using ( when it comes to development of 32bit microcontrollers ) is STM32CubeMX. If you have not clue what that tool does – in short – it is GUI which allows for quick configuration of microcontroller and its peripheral. It is nice at the end to generate you the code in selected IDE project format ( we will come back to alternate use of this in one of next posts )

Download the software

We will start off with downloading the software. Go STM32CubeMx download page  and scroll down to the bottom. You will find the download option for the software there



Page requires you to create account – but that is part which I leave completely up to you 😉


Install software

Once downloaded on our Mac it would be great to just go ahead and install. If you open folder you will see the following files.



For obvious reasons you will start the ‘application’ file which in return will open the installer



The installation is like ‘camera’ – just next => next=> done! The only thing you need to do now is to open it and use it features!


Use the app



I hope this will get you and your toolkit on the right track! Enjoy coding!





PowerShell – Azure Resource Manager policies

Microsoft does not stop listening to people. Many of IT professionals is heavily using Azure Resource Manager and the natural course of action is to require better control over what can and what cannot be done.

Simple as it may sound Microsoft has now offered ARM policies.  You may find details from 23:22 min on video below


From the good side Microsoft has already prepared documentation for us which is waiting here.

Is it difficult ? I personally think it is not – altough there is no GUI but who from Engineers this days uses GUI 🙂 you have option to use either REST API or PowerShell cmdlets (communicating over that API 🙂 )

What polciies gives me control over ? It is build over the following principal :

  "if" : {
    <condition> | <logical operator>
  "then" : {
    "effect" : "deny | audit"

As you can see we define conditions and operators and based on that we take action like allow or deny.


At the moment I’m not droping any extra examples – as documentation have already couple of them – so you might to try them out as you read the details.


Happy automating 🙂


Powershell with Azure – deploy IaaS with Azure Resource Manager

Thesedays managing cloud should be somethining that is well automated and what you can be really comfortable with. Microsoft Azure when using Azure Resource Manager allows you to manage infrastructure via APIs or via Powershell (which is calling webApis then ).

I have to say that both of the approaches are quite nice. I have already worked some time ago on ARM json templates ( using Visual Studio Addon ) and they enable you to perform advanced operations in declarative way.

Good news is that we can also do that with Powershell as well. I’m aware that all over internet you can find ready scripts that will do deployments with a click of a button 🙂 but it goes about excercising 🙂 as thats how we learn.

First what you should make sure of is that you have Azure Powershell module installed. For now I always have been using WebPlatform installer . Once installed you should have it listed when query for modules

PS S:\get-module *azure* -listavailable
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   0.9.8      Azure                               {Disable-AzureServiceProjectRemoteDesktop, Enable-AzureSer...


With the above being prerequisite we can continue and go further with our excercise. Our target will be to deploy 4 virtual machines. First of them will become a domain controller and should have static IP address. Remaining will be using dynamic addresses. Also we will not be creating availability groups and we will only have one public IP address ( we will investigate different setup in one of next posts ) which will expose 3389 port for us ( we will be restricting that via security groups altough )

Of course I dont have to remind you that for this you need valid Azure subscription ( but I assume you have one – even trial 🙂  ). The script as a whole is available via github and will be linked by the end of this post.


General setup

First we start of with setting up our azure subscription credentials and defining subscription details and amoount of VMs to be created.  Here we start off with getting our credentials (if we would use Azure AD and delegate credentials to newly created user we could pass PScredential object as argument ) . Later on we select one of available subscriptions (we can use out-grid to give enduser option of selecting ).



#region Setup subscription and script variables

## Get up your credentials (only if using Azure AD )
# credentialSubscription = Get-Credential 
# Add-AzureAccount -Credential 

## Enable debugging
$DebugPreference ='Continue'

## Login into your subscription

# using grid view select a subscription - in scripted scenario you will have the ID supplied to this script 
$subscriptionId =  (Get-AzureSubscription |Out-GridView -Title 'Choose your subscription' -PassThru).SubscriptionId

# Since user can click cancel if we dont have a subscriptionId we quit - again only for non automated scenario 
if ( [string]::IsNullOrEmpty( $subscriptionId ) ) { return }

# If you have more than 1 subscription associated it might be handy to choose current :) 
Select-AzureSubscription -SubscriptionId $subscriptionId -Current

## Switch to ARM - be aware that this functionality will be deprecated 
## https://github.com/Azure/azure-powershell/wiki/Deprecation-of-Switch-AzureMode-in-Azure-PowerShell 
Switch-AzureMode AzureResourceManager

## Check available locations 
$azureLocations= Get-AzureLocation 

## Check which locations we have available 
($azureLocations | Where-Object Name -eq ResourceGroup).Locations

## Select one location (we could use the grid view however I will deploy in West Europe)
$selectedAzureLocation = 'West Europe'

## Check registrered providers - also useful when looking for resources 

## Define resources prefix that we will use across this script 
## If this would be coming from variable outside of script we would need to make sure its lowercase 
$rscprefix = ('armdeploy').ToLower()

## Create tags to be used later 
$tags = New-Object System.Collections.ArrayList
$tags.Add( @{ Name = 'project'; Value = 'armdeploy' } )
$tags.Add( @{ Name = 'env'; Value = 'demo' } )

## Another way to create tags 
$tags = @( @{ Name='project'; Value='armdeploy' }, @{ Name='env'; Value='demo'} )

## Number of VMs to create 



What is the most important here is the part of switching azure operations mode done with :

Switch-AzureMode AzureResourceManager

This command have been deprecated and will not be available in future! Please take a look at post here for more detailed information!

And since I like to be in control of whats going on I tend to change output to be more verbose on debug side. This is done easily bu specyfying :

## Enable debugging
$DebugPreference ='Continue'


Create resource group

Within Azure nowadays we have concept of resource groups which are form of “containers” for keeping resources related to each other together. So if we want to create new obects we must start with resource group. Creating of it its quite easy.

#region Create Azure Resource Group

## Prepare resource group name 
$rscgName = "rg-${rscprefix}"

## Check if our resource group exists 
If (!(Test-AzureResourceGroup -ResourceGroupName $rscgName))
        # Does not exist - create it - also set up deployment name
        $resourceGroup = New-AzureResourceGroup -Name $rscgName -Location $selectedAzureLocation -Tag $tags -DeploymentName "deploy-${rscgName}"
        # Exists - get the resource by resource group name
        $resourceGroup = Get-AzureResourceGroup -Name $rscgName



Through the rest of the post you will see me checking for resources using Test-<typeOfResource> however looking at gitHub shows that some of those are depracated as well. So it migth be that this part will require a bit of rework.

Create storage account

In order to store OS and data disks we must have object within azure. And here we can utilize Azure Storage accounts. For this we create account – but in real life scenario you would just go ahead and use existing one for example.

#region Create Azure Storage Account

# Since we need to store the virutal machine data somewhere we need storage account 
# we can script in a way that if it does not exist it will be created within our resource group

## Storage account name 

## handy method to find valid types for storage account type

# First we get our command 
$gc= get-command New-AzureStorageAccount

# Then we navigate to property holding attributes which are TypeId of System.Management.Automation.ValidateSetAttribute 
# so it is clearly visible that those will be validate set values 

# Based on the above lets choose a type for the storage account 
$saType = 'Standard_LRS'

## Here we will check if we have storage account within our resource group
if (!(Test-AzureResource -ResourceName $saName -ResourceType 'Microsoft.Storage/storageAccounts' -ResourceGroupName $resourceGroup)) 
        # No storage account so lets go ahead and create it based on parameters we have above
        $sa = New-AzureStorageAccount -Name $saName -ResourceGroupName $resourceGroup.ResourceGroupName -Location $selectedAzureLocation -Type $saType

        # Storage account exists - lets grab its resource 
        $storageAccount = Get-AzureStorageAccount -ResourceGroupName $resourceGroup -Name $saName

## Once this is completed lets set subscription storage account since we do have ID already 
Set-AzureSubscription -SubscriptionId $subscriptionId -CurrentStorageAccountName $saName



Create virtual networks

In order to have networking running properly you need network. I really like concept of virtual networks and subnets and with this connecting directly with network interfaces and other objects things start to make sense – it all interconnects 🙂

#region Create Azure Virtual Network

$vnetName = "vnet-${rscprefix}"

$vNetsubnet1Name = "${rscprefix}-subnet1"

$vNetsubnet2Name = "${rscprefix}-subnet2"

# Create Virtual Network if it doesn't exist
if (!(Test-AzureResource -ResourceName $vnetName -ResourceType 'Microsoft.Network/virtualNetworks' -ResourceGroupName $resourceGroup.ResourceGroupName)) 
    # Create first subnet 
    $vSubnet1 = New-AzureVirtualNetworkSubnetConfig -Name $vNetsubnet1Name -AddressPrefix ''

    # Create second subnet
    $vSubnet2 = New-AzureVirtualNetworkSubnetConfig -Name $vNetsubnet2Name -AddressPrefix ''

    # Create virtual network
    $vNetwork = New-AzureVirtualNetwork -Name $vnetName -ResourceGroupName $resourceGroup.ResourceGroupName -Location $selectedAzureLocation -AddressPrefix '' -Subnet $vSubnet1, $vSubnet2 -Tag $tags


    # retrieve virtual network if already exists
    $vNetwork = Get-AzureVirtualNetwork -Name $vnetName -ResourceGroupName $resourceGroup.ResourceGroupName



You can see in above that I create 2 subnets. Altough I could get away with one – next one we might use in upcoming posts


Create public IP address

As mentioned before – I’m after a really simple set up here. So I will just create single public IP address (and make sure it is resolvable with DNS ) which I will be using later to connect to VMs

#region Create Azure Public IP address

$publicIPname = "pip-${rscprefix}" # PublicIP => pip

$publicIPdns = "dns-${rscprefix}"  # this will be our DNS name

## Here we check pi
$retryCntDns = 0

    $domainAvailable = ( Test-AzureDnsAvailability -DomainQualifiedName $publicIPdns -Location $selectedAzureLocation )
while(!$domainAvailable -or $retryCntDns -eq 3  ) 

# Check if we have our resource already existing 
if (!(Test-AzureResource -ResourceName $publicIPname -ResourceType 'Microsoft.Network/publicIPAddresses' -ResourceGroupName $resourceGroup.ResourceGroupName)) 
    If (!$domainAvailable)
        # we dont have public domain available here - we create without DNS entry
        $publicIp = New-AzurePublicIpAddress -Name $publicIPname -ResourceGroupName $resourceGroup.ResourceGroupName -Location $selectedAzureLocation -AllocationMethod Dynamic -Tag $tags
        # We do have dns available - lets create it with DNS name
        $publicIp = New-AzurePublicIpAddress -Name $publicIPname -ResourceGroupName $resourceGroup.ResourceGroupName -Location $selectedAzureLocation -AllocationMethod Dynamic -DomainNameLabel $publicIPdns -Tag $tags

    # Seems like we have already public IP address so we can just go ahead and retrieve it
    $publicIp = Get-AzurePublicIpAddress -Name $publicIPname -ResourceGroupName $resourceGroup.ResourceGroupName



Create network security group

To provide security we can now define ACLs on objects like subnets / network interfaces which allows us to have granular security. Below I will just create one for remote desktop access (in this example allow from any destination – which is not a good thing in production )

#region Create Network Security Group & Rules

# Define unique name for NSG resource
$nsgName = "nsg-${rscprefix}"

if (!(Test-AzureResource -ResourceName $nsgName -ResourceType 'Microsoft.Network/networkSecurityGroups' -ResourceGroupName $resourceGroup.ResourceGroupName)) 

    # Create RDP access rule (at the script allow from everywhere - you should investigate this for your environment security)
    $nsgRule_RDP = New-AzureNetworkSecurityRuleConfig `
        -Name 'allow-in-rdp' `
        -Description 'Allow Remote Desktop Access' `
        -SourceAddressPrefix * `
        -DestinationAddressPrefix * `
        -Protocol Tcp `
        -SourcePortRange * `
        -DestinationPortRange 3389 `
        -Direction Inbound `
        -Access Allow `
        -Priority 100

    # Create Network Security Group with Rule above 
    $nsg = New-AzureNetworkSecurityGroup -Name $nsgName -ResourceGroupName $resourceGroup.ResourceGroupName -Location $selectedAzureLocation -SecurityRules $nsgRule_RDP -Tag $tags

    # Get NSG if already created
    $nsg = Get-AzureNetworkSecurityGroup -Name $nsgName -ResourceGroupName $resourceGroup.ResourceGroupName



Create network interfaces

Now to all connect to each other we create network interfaces. To first interface we will add additionaly our public IP address

#region Define network interfaces 

$networkInterfaces = @() # We will use this array to hold our network interfaces

# For each VM we will create a network interface

for ($count = 1; $count -le $VMcount; $count++) 

    $nicName = "${rscprefix}-nic${count}"

    if (!(Test-AzureResource -ResourceName $nicName -ResourceType 'Microsoft.Network/networkInterfaces' -ResourceGroupName $resourceGroup.ResourceGroupName)) 

        $nicIndex = $count – 1
        # The first VM will be our domain controller/DNS and it needs static IP address 
        if ($count -eq 1)
            $networkInterfaces += New-AzureNetworkInterface -Name $nicName -ResourceGroupName $resourceGroup.ResourceGroupName -Location $selectedAzureLocation -SubnetId $vNetwork.Subnets[0].Id -NetworkSecurityGroupId $nsg.Id -IpConfigurationName 'ipconfig-dc01' -PrivateIpAddress -PublicIpAddressId $publicIp.Id
            $networkInterfaces += New-AzureNetworkInterface -Name $nicName -ResourceGroupName $resourceGroup.ResourceGroupName -Location $selectedAzureLocation -SubnetId $vNetwork.Subnets[0].Id -NetworkSecurityGroupId $nsg.Id

        # retrieve existing
        $networkInterfaces += Get-AzureNetworkInterface -Name $nicName -ResourceGroupName $resourceGroup.ResourceGroupName




Provision VMs

And now the time has come to finally provision virtual machines based on resources we got prepared for this.

#region Provision virtual machines

## If you would like to you could use those to present enduser (or yourself) with visual option to choose publisher/offer and SKU 
## as this is scripted version example we will use hardcoded values 

#$publisherName = ( Get-AzureVMImagePublisher -Location $selectedAzureLocation ).PublisherName | Out-GridView -Title 'Select a VM Image Publisher ...'  -PassThru
#$offerName = ( Get-AzureVMImageOffer -PublisherName $publisherName -Location $selectedAzureLocation ).Offer | Out-GridView -Title 'Select a VM Image Offer ...' -PassThru
#$skuName = ( Get-AzureVMImageSku -PublisherName $publisherName -Offer $offerName -Location $selectedAzureLocation ).Skus |Out-GridView -Title 'Select a VM Image SKU' -PassThru

$publisherName = 'MicrosoftWindowsServer'

# Take latest version
$version = 'latest'

# We will use basic version of VMs - later we will be able to provision it further
$vmSize = 'Basic_A1'

# Get credentials for admin account - you may want to modify username
$vmAdminCreds = Get-Credential Adminowski -Message 'Provide credentials for admin account'

# array to hold VMs
$vm = @()

# Create VMs
for ($count = 1; $count -le $VMcount; $count++) 
    # create suffixed VM name
    $vmName = "vm-${count}"

    # Check if resource already exists
    if (!(Test-AzureResource -ResourceName $vmName -ResourceType 'Microsoft.Compute/virtualMachines' -ResourceGroupName $resourceGroup.ResourceGroupName)) 
        $vmIndex = $count - 1

        $osDiskLabel = 'OSDisk'
        $osDiskName = "${rscprefix}-${vmName}-osdisk"

        $osDiskUri = $sa.PrimaryEndpoints.Blob.ToString() + "vhds/${osDiskName}.vhd"

        $dataDiskSize = 200 # Size in GB

        $dataDiskLabel = 'DataDisk01'

        $dataDiskName = "${rscprefix}-${vmName}-datadisk01"

        $dataDiskUri = $sa.PrimaryEndpoints.Blob.ToString() + "vhds/${dataDiskName}.vhd"

        $vmConfig =  New-AzureVMConfig -VMName $vmName -VMSize $vmSize | `
            Set-AzureVMOperatingSystem `
                -Windows `
                -ComputerName $vmName `
                -Credential $vmAdminCreds `
                -ProvisionVMAgent `
                -EnableAutoUpdate |
            Set-AzureVMSourceImage `
                -PublisherName $publisherName `
                -Offer $offerName `
                -Skus $skuName `
                -Version $version |
            Set-AzureVMOSDisk `
                -Name $osDiskLabel `
                -VhdUri $osDiskUri `
                -CreateOption fromImage |
            Add-AzureVMDataDisk `
                -Name $dataDiskLabel `
                -DiskSizeInGB $dataDiskSize `
                -VhdUri $dataDiskURI `
                -CreateOption empty |
            Add-AzureVMNetworkInterface `
                -Id $networkInterfaces[$vmIndex].Id `

        New-AzureVM `
            -VM $vmConfig `
            -ResourceGroupName $resourceGroup.ResourceGroupName `
            -Location $selectedAzureLocation `
            -Tags $tags

        # Get the VM if already provisioned
        $vm += Get-AzureVM -Name $vmName -ResourceGroupName $resourceGroup.ResourceGroupName




Look at completed action

Once the whole script completes we get direct access to our newly created reources . It looks good and is a worth noting starting point for autoation and orchestration. From here next logical step is to describe how this infrastructure should be configured with DSC and that is something we will do with on of our next posts.




Happy powershelling 🙂




Script in full