diff --git a/.gitignore b/.gitignore
index 5e1422c..de6b2db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,24 +10,10 @@
/test/version_tmp/
/tmp/
-# Used by dotenv library to load environment variables.
-# .env
-
## Specific to RubyMotion:
.dat*
.repl_history
build/
-*.bridgesupport
-build-iPhoneOS/
-build-iPhoneSimulator/
-
-## Specific to RubyMotion (use of CocoaPods):
-#
-# We recommend against adding the Pods directory to your .gitignore. However
-# you should judge for yourself, the pros and cons are mentioned at:
-# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
-#
-# vendor/Pods/
## Documentation cache and generated files:
/.yardoc/
@@ -48,3 +34,7 @@ build-iPhoneSimulator/
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
+
+/.idea/
+.env
+
diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000..83e16f8
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,2 @@
+--color
+--require spec_helper
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 0000000..d1b39c3
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+ruby@compute-ruby-msi-vm
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9824752..7e36d76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,5 @@
-## [project-title] Changelog
-
-
-# x.y.z (yyyy-mm-dd)
-
-*Features*
-* ...
-
-*Bug Fixes*
-* ...
-
-*Breaking Changes*
-* ...
+ ## 2017.09.12
+
+ *Features*
+ * Adding initial sample to create Managed Service Identity Azure virtual machine.
+
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 4bd3eba..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Contributing to [project-title]
-
-This project welcomes contributions and suggestions. Most contributions require you to agree to a
-Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
-the rights to use your contribution. For details, visit https://cla.microsoft.com.
-
-When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
-a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
-provided by the bot. You will only need to do this once across all repos using our CLA.
-
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
-contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
-
- - [Code of Conduct](#coc)
- - [Issues and Bugs](#issue)
- - [Feature Requests](#feature)
- - [Submission Guidelines](#submit)
-
-## Code of Conduct
-Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-
-## Found an Issue?
-If you find a bug in the source code or a mistake in the documentation, you can help us by
-[submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can
-[submit a Pull Request](#submit-pr) with a fix.
-
-## Want a Feature?
-You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub
-Repository. If you would like to *implement* a new feature, please submit an issue with
-a proposal for your work first, to be sure that we can use it.
-
-* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
-
-## Submission Guidelines
-
-### Submitting an Issue
-Before you submit an issue, search the archive, maybe your question was already answered.
-
-If your issue appears to be a bug, and hasn't been reported, open a new issue.
-Help us to maximize the effort we can spend fixing issues and adding new
-features, by not reporting duplicate issues. Providing the following information will increase the
-chances of your issue being dealt with quickly:
-
-* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
-* **Version** - what version is affected (e.g. 0.1.2)
-* **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you
-* **Browsers and Operating System** - is this a problem with all browsers?
-* **Reproduce the Error** - provide a live example or a unambiguous set of steps
-* **Related Issues** - has a similar issue been reported before?
-* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
- causing the problem (line of code or commit)
-
-You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new].
-
-### Submitting a Pull Request (PR)
-Before you submit your Pull Request (PR) consider the following guidelines:
-
-* Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR
- that relates to your submission. You don't want to duplicate effort.
-
-* Make your changes in a new git fork:
-
-* Commit your changes using a descriptive commit message
-* Push your fork to GitHub:
-* In GitHub, create a pull request
-* If we suggest changes then:
- * Make the required updates.
- * Rebase your fork and force push to your GitHub repository (this will update your Pull Request):
-
- ```shell
- git rebase master -i
- git push -f
- ```
-
-That's it! Thank you for your contribution!
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..1fc1bb0
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,21 @@
+# encoding: utf-8
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+
+source 'https://rubygems.org'
+
+group :development, :test do
+ gem 'rake', '~>11.1'
+ gem 'rspec', '~>3.4'
+ gem 'dotenv', '~>2.1'
+ gem 'vcr', '~>3.0'
+ gem 'webmock', '~>2.0'
+ gem 'climate_control'
+end
+
+gem 'azure_mgmt_authorization', '~>0.12.0'
+gem 'azure_mgmt_resources', '~>0.12.0'
+gem 'azure_mgmt_compute', '~>0.12.0'
+gem 'azure_mgmt_network', '~>0.12.0'
+gem 'azure_mgmt_storage', '~>0.12.0'
+gem 'haikunator', '~>1.1'
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..a25ed63
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,87 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.5.1)
+ public_suffix (~> 2.0, >= 2.0.2)
+ azure_mgmt_authorization (0.12.0)
+ ms_rest_azure (~> 0.9.0)
+ azure_mgmt_compute (0.12.0)
+ ms_rest_azure (~> 0.9.0)
+ azure_mgmt_network (0.12.0)
+ ms_rest_azure (~> 0.9.0)
+ azure_mgmt_resources (0.12.0)
+ ms_rest_azure (~> 0.9.0)
+ azure_mgmt_storage (0.12.0)
+ ms_rest_azure (~> 0.9.0)
+ climate_control (0.2.0)
+ concurrent-ruby (1.0.5)
+ crack (0.4.3)
+ safe_yaml (~> 1.0.0)
+ diff-lcs (1.3)
+ domain_name (0.5.20170404)
+ unf (>= 0.0.5, < 1.0.0)
+ dotenv (2.2.1)
+ faraday (0.13.1)
+ multipart-post (>= 1.2, < 3)
+ faraday-cookie_jar (0.0.6)
+ faraday (>= 0.7.4)
+ http-cookie (~> 1.0.0)
+ haikunator (1.1.0)
+ hashdiff (0.3.5)
+ http-cookie (1.0.3)
+ domain_name (~> 0.5)
+ ms_rest (0.7.1)
+ concurrent-ruby (~> 1.0)
+ faraday (~> 0.9)
+ timeliness (~> 0.3)
+ ms_rest_azure (0.9.0)
+ concurrent-ruby (~> 1.0)
+ faraday (~> 0.9)
+ faraday-cookie_jar (~> 0.0.6)
+ ms_rest (~> 0.7.0)
+ multipart-post (2.0.0)
+ public_suffix (2.0.5)
+ rake (11.3.0)
+ rspec (3.6.0)
+ rspec-core (~> 3.6.0)
+ rspec-expectations (~> 3.6.0)
+ rspec-mocks (~> 3.6.0)
+ rspec-core (3.6.0)
+ rspec-support (~> 3.6.0)
+ rspec-expectations (3.6.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.6.0)
+ rspec-mocks (3.6.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.6.0)
+ rspec-support (3.6.0)
+ safe_yaml (1.0.4)
+ timeliness (0.3.8)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.4)
+ vcr (3.0.3)
+ webmock (2.3.2)
+ addressable (>= 2.3.6)
+ crack (>= 0.3.2)
+ hashdiff
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ azure_mgmt_authorization (~> 0.12.0)
+ azure_mgmt_compute (~> 0.12.0)
+ azure_mgmt_network (~> 0.12.0)
+ azure_mgmt_resources (~> 0.12.0)
+ azure_mgmt_storage (~> 0.12.0)
+ climate_control
+ dotenv (~> 2.1)
+ haikunator (~> 1.1)
+ rake (~> 11.1)
+ rspec (~> 3.4)
+ vcr (~> 3.0)
+ webmock (~> 2.0)
+
+BUNDLED WITH
+ 1.14.3
diff --git a/README.md b/README.md
index 5efee44..80ee816 100644
--- a/README.md
+++ b/README.md
@@ -1,57 +1,337 @@
-# Project Name
+---
+services: compute
+platforms: ruby
+author: vishrutshah
+---
-(short, 1-3 sentenced, description of the project)
+# Create Azure virtual machines with Managed Service Identity using Ruby
+This sample demonstrates how to create your Azure virtual machines with Managed Service Identity using the Ruby SDK.
-## Features
+**On this page**
-This project framework provides the following features:
+- [Run this sample](#run)
+- [What is example.rb doing?](#example)
+ - [Create a virtual network](#vnet)
+ - [Create a public IP address](#ipaddress)
+ - [Create a network interface](#nic)
+ - [Create a virtual machine with system identity](#vm)
+ - [Add MSI extension to the VM](#extension)
+ - [Create role assignment for the VM](#role-assignment)
+ - [Verify MSI extension is running on VM by logging-in via ssh](#msi-extension)
+ - [Delete the resources](#delete)
-* Feature 1
-* Feature 2
-* ...
-## Getting Started
+
+## Run this sample
-### Prerequisites
+1. If you don't already have it, [install Ruby and the Ruby DevKit](https://www.ruby-lang.org/en/documentation/installation/).
-(ideally very short, if any)
+2. If you don't have bundler, install it.
-- OS
-- Library version
-- ...
+ ```
+ gem install bundler
+ ```
+
+3. Clone the repository.
-### Installation
+ ```
+ git clone https://github.com/Azure-Samples/compute-ruby-msi-vm.git
+ ```
-(ideally very short)
+4. Install the dependencies using bundle.
-- npm install [package name]
-- mvn install
-- ...
+ ```
+ cd compute-ruby-msi-vm
+ bundle install
+ ```
+
+5. Create an Azure service principal either through
-### Quickstart
-(Add steps to get up and running quickly)
+ [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?toc=%2Fazure%2Fazure-resource-manager%2Ftoc.json&view=azure-cli-latest),
+ [PowerShell](https://azure.microsoft.com/documentation/articles/resource-group-authenticate-service-principal/)
+ or [the portal](https://azure.microsoft.com/documentation/articles/resource-group-create-service-principal-portal/).
-1. git clone [repository clone url]
-2. cd [respository name]
-3. ...
+ :exclamation: NOTE :exclamation: Please make sure to create an role assignment with `Owner` role. In case of insufficient permissions, role
+ assignment may fail for this sample.
+6. Set the following environment variables using the information from the service principle that you created.
-## Demo
+ ```
+ export AZURE_TENANT_ID={your tenant id}
+ export AZURE_CLIENT_ID={your client id}
+ export AZURE_CLIENT_SECRET={your client secret}
+ export AZURE_SUBSCRIPTION_ID={your subscription id}
+ ```
-A demo app is included to show how to use the project.
+ > [AZURE.NOTE] On Windows, use `set` instead of `export`.
-To run the demo, follow these steps:
+7. Run the sample.
-(Add steps to start up the demo)
+ ```
+ bundle exec ruby example.rb
+ ```
-1.
-2.
-3.
+
+## What does example.rb doing?
-## Resources
+This sample starts by setting up ResourceManagementClient, the resource provider clients, a resource group, and a storage account using your subscription and credentials.
-(Any additional resources or related projects)
+```ruby
+subscription_id = ENV['AZURE_SUBSCRIPTION_ID'] || '11111111-1111-1111-1111-111111111111' # your Azure Subscription Id
+ provider = MsRestAzure::ApplicationTokenProvider.new(
+ ENV['AZURE_TENANT_ID'],
+ ENV['AZURE_CLIENT_ID'],
+ ENV['AZURE_CLIENT_SECRET'])
+ credentials = MsRest::TokenCredentials.new(provider)
+ resource_client = Azure::ARM::Resources::ResourceManagementClient.new(credentials)
+ resource_client.subscription_id = subscription_id
+ network_client = Azure::ARM::Network::NetworkManagementClient.new(credentials)
+ network_client.subscription_id = subscription_id
+ storage_client = Azure::ARM::Storage::StorageManagementClient.new(credentials)
+ storage_client.subscription_id = subscription_id
+ compute_client = Azure::ARM::Compute::ComputeManagementClient.new(credentials)
+ compute_client.subscription_id = subscription_id
-- Link to supporting information
-- Link to similar sample
-- ...
+ #
+ # Managing resource groups
+ #
+ resource_group_params = Azure::ARM::Resources::Models::ResourceGroup.new.tap do |rg|
+ rg.location = WEST_US
+ end
+
+ # Create Resource group
+ puts 'Create Resource Group'
+ print_group resource_client.resource_groups.create_or_update(GROUP_NAME, resource_group_params)
+
+ postfix = rand(1000)
+ storage_account_name = "rubystor#{postfix}"
+ puts "Creating a premium storage account with encryption off named #{storage_account_name} in resource group #{GROUP_NAME}"
+ storage_create_params = StorageModels::StorageAccountCreateParameters.new.tap do |account|
+ account.location = WEST_US
+ account.sku = StorageModels::Sku.new.tap do |sku|
+ sku.name = StorageModels::SkuName::PremiumLRS
+ sku.tier = StorageModels::SkuTier::Premium
+ end
+ account.kind = StorageModels::Kind::Storage
+ account.encryption = StorageModels::Encryption.new.tap do |encrypt|
+ encrypt.services = StorageModels::EncryptionServices.new.tap do |services|
+ services.blob = StorageModels::EncryptionService.new.tap do |service|
+ service.enabled = false
+ end
+ end
+ end
+ end
+ print_item storage_account = storage_client.storage_accounts.create(GROUP_NAME, storage_account_name, storage_create_params)
+```
+
+
+### Create a virtual network
+Now, we will create a virtual network and configure subnet for the virtual machine.
+
+```ruby
+puts 'Creating a virtual network for the VM'
+vnet_create_params = NetworkModels::VirtualNetwork.new.tap do |vnet|
+ vnet.location = WEST_US
+ vnet.address_space = NetworkModels::AddressSpace.new.tap do |addr_space|
+ addr_space.address_prefixes = ['10.0.0.0/16']
+ end
+ vnet.dhcp_options = NetworkModels::DhcpOptions.new.tap do |dhcp|
+ dhcp.dns_servers = ['8.8.8.8']
+ end
+ vnet.subnets = [
+ NetworkModels::Subnet.new.tap do |subnet|
+ subnet.name = 'rubySampleSubnet'
+ subnet.address_prefix = '10.0.0.0/24'
+ end
+ ]
+end
+print_item vnet = network_client.virtual_networks.create_or_update(GROUP_NAME, 'sample-ruby-vnet', vnet_create_params)
+```
+
+
+### Create a public IP address
+Now, we will create a public IP address using dynamic IP allocation method for the Azure VM.
+
+```ruby
+puts 'Creating a public IP address for the VM'
+public_ip_params = NetworkModels::PublicIPAddress.new.tap do |ip|
+ ip.location = WEST_US
+ ip.public_ipallocation_method = NetworkModels::IPAllocationMethod::Dynamic
+ ip.dns_settings = NetworkModels::PublicIPAddressDnsSettings.new.tap do |dns|
+ dns.domain_name_label = 'msi-vm-domain-name-label'
+ end
+end
+print_item public_ip = network_client.public_ipaddresses.create_or_update(GROUP_NAME, 'sample-ruby-pubip', public_ip_params)
+```
+
+
+### Create a network interface
+Now, we will create a network interface and assign the public ip address created in previous step.
+
+```ruby
+print_item nic = network_client.network_interfaces.create_or_update(
+ GROUP_NAME,
+ "sample-ruby-nic-#{vm_name}",
+ NetworkModels::NetworkInterface.new.tap do |interface|
+ interface.location = WEST_US
+ interface.ip_configurations = [
+ NetworkModels::NetworkInterfaceIPConfiguration.new.tap do |nic_conf|
+ nic_conf.name = "sample-ruby-nic-#{vm_name}"
+ nic_conf.private_ipallocation_method = NetworkModels::IPAllocationMethod::Dynamic
+ nic_conf.subnet = subnet
+ nic_conf.public_ipaddress = public_ip
+ end
+ ]
+ end
+)
+```
+
+
+### Create a virtual machine with system identity
+Now, we will set virtual machine parameters like `OSProfile`, `StorageProfile`, `OSDisk`, `HardwareProfile` & `NetworkProfile` as usual. We will
+also set the `VirtualMachineIdentity` to be `SystemAssigned` specifically for creating managed service identity VM and then create the virtual machine.
+
+```ruby
+puts 'Creating a Ubuntu 16.04.0-LTS Standard DS2 V2 virtual machine w/ a public IP'
+vm_create_params = ComputeModels::VirtualMachine.new.tap do |vm|
+ vm.location = location
+ vm.os_profile = ComputeModels::OSProfile.new.tap do |os_profile|
+ os_profile.computer_name = vm_name
+ os_profile.admin_username = 'notAdmin'
+ os_profile.admin_password = 'Pa$$w0rd92'
+ end
+
+ vm.storage_profile = ComputeModels::StorageProfile.new.tap do |store_profile|
+ store_profile.image_reference = ComputeModels::ImageReference.new.tap do |ref|
+ ref.publisher = 'canonical'
+ ref.offer = 'UbuntuServer'
+ ref.sku = '16.04.0-LTS'
+ ref.version = 'latest'
+ end
+ store_profile.os_disk = ComputeModels::OSDisk.new.tap do |os_disk|
+ os_disk.name = "sample-os-disk-#{vm_name}"
+ os_disk.caching = ComputeModels::CachingTypes::None
+ os_disk.create_option = ComputeModels::DiskCreateOptionTypes::FromImage
+ os_disk.vhd = ComputeModels::VirtualHardDisk.new.tap do |vhd|
+ vhd.uri = "https://#{storage_acct.name}.blob.core.windows.net/rubycontainer/#{vm_name}.vhd"
+ end
+ end
+ end
+
+ vm.hardware_profile = ComputeModels::HardwareProfile.new.tap do |hardware|
+ hardware.vm_size = ComputeModels::VirtualMachineSizeTypes::StandardDS2V2
+ end
+
+ vm.network_profile = ComputeModels::NetworkProfile.new.tap do |net_profile|
+ net_profile.network_interfaces = [
+ ComputeModels::NetworkInterfaceReference.new.tap do |ref|
+ ref.id = nic.id
+ ref.primary = true
+ end
+ ]
+ end
+
+ # Use System Assigned Identity for the VM
+ vm.identity = ComputeModels::VirtualMachineIdentity.new.tap do |identity|
+ identity.type = ComputeModels::ResourceIdentityType::SystemAssigned
+ end
+end
+
+ssh_pub_location = File.expand_path('~/.ssh/id_rsa.pub')
+if File.exists? ssh_pub_location
+ puts "Found SSH public key in #{ssh_pub_location}. Disabling password and enabling SSH authentication."
+ key_data = File.read(ssh_pub_location)
+ puts "Using public key: #{key_data}"
+ vm_create_params.os_profile.linux_configuration = ComputeModels::LinuxConfiguration.new.tap do |linux|
+ linux.disable_password_authentication = true
+ linux.ssh = ComputeModels::SshConfiguration.new.tap do |ssh_config|
+ ssh_config.public_keys = [
+ ComputeModels::SshPublicKey.new.tap do |pub_key|
+ pub_key.key_data = key_data
+ pub_key.path = '/home/notAdmin/.ssh/authorized_keys'
+ end
+ ]
+ end
+ end
+end
+
+print_item vm = compute_client.virtual_machines.create_or_update(GROUP_NAME, "sample-ruby-vm-#{vm_name}", vm_create_params)
+```
+
+
+### Add MSI extension to the VM
+Now, we will add an VM extension `ManagedIdentityExtensionForLinux` for Azure VM and configure it to run on port `50342`.
+
+```ruby
+puts "Install Managed Service Identity Extension"
+ext_name = 'msiextension'
+vm_extension = ComputeModels::VirtualMachineExtension.new.tap do |extension|
+ extension.publisher = 'Microsoft.ManagedIdentity'
+ extension.virtual_machine_extension_type = 'ManagedIdentityExtensionForLinux'
+ extension.type_handler_version = '1.0'
+ extension.auto_upgrade_minor_version = true
+ extension.settings = Hash.new.tap do |settings|
+ settings['port'] = '50342'
+ end
+ extension.location = WEST_US
+end
+
+vm_ext = compute_client.virtual_machine_extensions.create_or_update(GROUP_NAME, "sample-ruby-vm-#{vm_name}", ext_name, vm_extension)
+```
+
+
+### Create role assignment for the VM
+Now, we will retrieve the default rbac role named as `Contributor`. To know more about the
+default roles please visit [built-in-roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles).
+
+```ruby
+puts "Getting the Role ID of Contributor of a Resource group: #{GROUP_NAME}"
+role_name = 'Contributor'
+roles = authorization_client.role_definitions.list(resource_group.id, "roleName eq '#{role_name}'")
+contributor_role = roles.first
+```
+
+Now, we will assign `Contributor` role at the resource group level to allow managing Azure resources inside
+this resource group.
+
+```ruby
+puts 'Creating the role assignment for the VM'
+role_assignment_params = AuthorizationModels::RoleAssignmentCreateParameters.new.tap do |role_param|
+ role_param.properties = AuthorizationModels::RoleAssignmentProperties.new.tap do |property|
+ property.principal_id = vm.identity.principal_id
+ property.role_definition_id = contributor_role.id
+ end
+end
+authorization_client.role_assignments.create(resource_group.id, SecureRandom.uuid, role_assignment_params)
+```
+
+
+### Verify MSI extension is running on VM by logging-in via ssh
+Once the Azure VM has been created, we will verify that MSI extension is running on this VM. Managed Service Identity extension will run on
+`localhost` and configured port, here `50342`. Follow example [here](https://github.com/Azure/azure-sdk-for-ruby/blob/master/runtime/ms_rest_azure/README.md#utilizing-msimanaged-service-identity-token-provider) to
+find out the usage.
+
+```
+ssh -p 22 notAdmin@msi-vm-domain-name-label.westus.cloudapp.azure.com
+```
+```
+notAdmin@msi-vm:~$ netstat -tlnp
+(Not all processes could be identified, non-owned process info
+ will not be shown, you would have to be root to see it all.)
+Active Internet connections (only servers)
+Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
+tcp 0 0 127.0.0.1:50342 0.0.0.0:* LISTEN -
+...
+
+```
+```
+exit
+```
+
+
+### Delete the resources
+Now, we will delete all the resources created using this example. Please comment this out to keep the resources alive in you Azure subscription.
+
+```ruby
+resource_client.resource_groups.delete(GROUP_NAME)
+```
\ No newline at end of file
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..822d746
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,4 @@
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec)
+task :default => :spec
diff --git a/example.rb b/example.rb
new file mode 100644
index 0000000..2c99cd7
--- /dev/null
+++ b/example.rb
@@ -0,0 +1,279 @@
+#!/usr/bin/env ruby
+
+require 'azure_mgmt_resources'
+require 'azure_mgmt_network'
+require 'azure_mgmt_storage'
+require 'azure_mgmt_compute'
+require 'azure_mgmt_authorization'
+
+WEST_US = 'westus'
+GROUP_NAME = 'azure-sample-compute-msi'
+
+StorageModels = Azure::ARM::Storage::Models
+NetworkModels = Azure::ARM::Network::Models
+ComputeModels = Azure::ARM::Compute::Models
+ResourceModels = Azure::ARM::Resources::Models
+AuthorizationModels = Azure::ARM::Authorization::Models
+
+# This sample shows how to create a Azure virtual machines with Managed Service Identity using the Azure Resource Manager APIs for Ruby.
+#
+# This script expects that the following environment vars are set:
+#
+# AZURE_TENANT_ID: with your Azure Active Directory tenant id or domain
+# AZURE_CLIENT_ID: with your Azure Active Directory Application Client ID
+# AZURE_CLIENT_SECRET: with your Azure Active Directory Application Secret
+# AZURE_SUBSCRIPTION_ID: with your Azure Subscription Id
+#
+def run_example
+ #
+ # Create the Resource Manager Client with an Application (service principal) token provider
+ #
+ subscription_id = ENV['AZURE_SUBSCRIPTION_ID'] || '11111111-1111-1111-1111-111111111111' # your Azure Subscription Id
+ provider = MsRestAzure::ApplicationTokenProvider.new(
+ ENV['AZURE_TENANT_ID'],
+ ENV['AZURE_CLIENT_ID'],
+ ENV['AZURE_CLIENT_SECRET'])
+ credentials = MsRest::TokenCredentials.new(provider)
+ resource_client = Azure::ARM::Resources::ResourceManagementClient.new(credentials)
+ resource_client.subscription_id = subscription_id
+ network_client = Azure::ARM::Network::NetworkManagementClient.new(credentials)
+ network_client.subscription_id = subscription_id
+ storage_client = Azure::ARM::Storage::StorageManagementClient.new(credentials)
+ storage_client.subscription_id = subscription_id
+ compute_client = Azure::ARM::Compute::ComputeManagementClient.new(credentials)
+ compute_client.subscription_id = subscription_id
+ authorization_client = Azure::ARM::Authorization::AuthorizationManagementClient.new(credentials)
+ authorization_client.subscription_id = subscription_id
+
+ #
+ # Managing resource groups
+ #
+ resource_group_params = Azure::ARM::Resources::Models::ResourceGroup.new.tap do |rg|
+ rg.location = WEST_US
+ end
+
+ # Create Resource group
+ puts 'Create Resource Group'
+ print_group resource_group = resource_client.resource_groups.create_or_update(GROUP_NAME, resource_group_params)
+
+ postfix = rand(1000)
+ storage_account_name = "rubystor#{postfix}"
+ puts "Creating a premium storage account with encryption off named #{storage_account_name} in resource group #{GROUP_NAME}"
+ storage_create_params = StorageModels::StorageAccountCreateParameters.new.tap do |account|
+ account.location = WEST_US
+ account.sku = StorageModels::Sku.new.tap do |sku|
+ sku.name = StorageModels::SkuName::PremiumLRS
+ sku.tier = StorageModels::SkuTier::Premium
+ end
+ account.kind = StorageModels::Kind::Storage
+ account.encryption = StorageModels::Encryption.new.tap do |encrypt|
+ encrypt.services = StorageModels::EncryptionServices.new.tap do |services|
+ services.blob = StorageModels::EncryptionService.new.tap do |service|
+ service.enabled = false
+ end
+ end
+ end
+ end
+ print_item storage_account = storage_client.storage_accounts.create(GROUP_NAME, storage_account_name, storage_create_params)
+
+ puts 'Creating a virtual network for the VM'
+ vnet_create_params = NetworkModels::VirtualNetwork.new.tap do |vnet|
+ vnet.location = WEST_US
+ vnet.address_space = NetworkModels::AddressSpace.new.tap do |addr_space|
+ addr_space.address_prefixes = ['10.0.0.0/16']
+ end
+ vnet.dhcp_options = NetworkModels::DhcpOptions.new.tap do |dhcp|
+ dhcp.dns_servers = ['8.8.8.8']
+ end
+ vnet.subnets = [
+ NetworkModels::Subnet.new.tap do |subnet|
+ subnet.name = 'rubySampleSubnet'
+ subnet.address_prefix = '10.0.0.0/24'
+ end
+ ]
+ end
+ print_item vnet = network_client.virtual_networks.create_or_update(GROUP_NAME, 'sample-ruby-vnet', vnet_create_params)
+
+ puts 'Creating a public IP address for the VM'
+ public_ip_params = NetworkModels::PublicIPAddress.new.tap do |ip|
+ ip.location = WEST_US
+ ip.public_ipallocation_method = NetworkModels::IPAllocationMethod::Dynamic
+ ip.dns_settings = NetworkModels::PublicIPAddressDnsSettings.new.tap do |dns|
+ dns.domain_name_label = 'msi-vm-domain-name-label'
+ end
+ end
+ print_item public_ip = network_client.public_ipaddresses.create_or_update(GROUP_NAME, 'sample-ruby-pubip', public_ip_params)
+
+ vm = create_vm(compute_client, network_client, WEST_US, 'msi-vm', storage_account, vnet.subnets[0], public_ip)
+
+ puts "Getting the Role ID of Contributor of a Resource group: #{GROUP_NAME}"
+ role_name = 'Contributor'
+ roles = authorization_client.role_definitions.list(resource_group.id, "roleName eq '#{role_name}'")
+ contributor_role = roles.first
+ puts contributor_role
+
+ puts 'Creating the role assignment for the VM'
+ role_assignment_params = AuthorizationModels::RoleAssignmentCreateParameters.new.tap do |role_param|
+ role_param.properties = AuthorizationModels::RoleAssignmentProperties.new.tap do |property|
+ property.principal_id = vm.identity.principal_id
+ property.role_definition_id = contributor_role.id
+ end
+ end
+ authorization_client.role_assignments.create(resource_group.id, SecureRandom.uuid, role_assignment_params)
+
+ puts 'Listing all of the resources within the group'
+ resource_client.resources.list_by_resource_group(GROUP_NAME).each do |res|
+ print_item res
+ end
+ puts ''
+
+ export_template(resource_client)
+
+ puts "Thank you for creating managed service identity Azure VM."
+ puts "Use `netstat -tlnp` command verify that MSI service is running at 127.0.0.1:50342 address."
+ puts "Connect to your new virtual machine via: 'ssh -p 22 #{vm.os_profile.admin_username}@#{public_ip.dns_settings.fqdn}'. Admin Password is: Pa$$w0rd92"
+
+ puts 'Press any key to continue and delete the sample resources'
+ gets
+
+ # Delete Resource group and everything in it
+ puts 'Delete Resource Group'
+ resource_client.resource_groups.delete(GROUP_NAME)
+ puts "\nDeleted: #{GROUP_NAME}"
+
+end
+
+def print_group(resource)
+ puts "\tname: #{resource.name}"
+ puts "\tid: #{resource.id}"
+ puts "\tlocation: #{resource.location}"
+ puts "\ttags: #{resource.tags}"
+ puts "\tproperties:"
+ print_item(resource.properties)
+end
+
+def print_item(resource)
+ resource.instance_variables.sort.each do |ivar|
+ str = ivar.to_s.gsub /^@/, ''
+ if resource.respond_to? str.to_sym
+ puts "\t\t#{str}: #{resource.send(str.to_sym)}"
+ end
+ end
+ puts "\n\n"
+end
+
+def export_template(resource_client)
+ puts "Exporting the resource group template for #{GROUP_NAME}"
+ export_result = resource_client.resource_groups.export_template(
+ GROUP_NAME,
+ ResourceModels::ExportTemplateRequest.new.tap{ |req| req.resources = ['*'] }
+ )
+ puts export_result.template
+ puts ''
+end
+
+# Create a Virtual Machine, Install MSI extension and return it
+def create_vm(compute_client, network_client, location, vm_name, storage_acct, subnet, public_ip)
+ puts "Creating a network interface for the VM #{vm_name}"
+ print_item nic = network_client.network_interfaces.create_or_update(
+ GROUP_NAME,
+ "sample-ruby-nic-#{vm_name}",
+ NetworkModels::NetworkInterface.new.tap do |interface|
+ interface.location = WEST_US
+ interface.ip_configurations = [
+ NetworkModels::NetworkInterfaceIPConfiguration.new.tap do |nic_conf|
+ nic_conf.name = "sample-ruby-nic-#{vm_name}"
+ nic_conf.private_ipallocation_method = NetworkModels::IPAllocationMethod::Dynamic
+ nic_conf.subnet = subnet
+ nic_conf.public_ipaddress = public_ip
+ end
+ ]
+ end
+ )
+
+ puts 'Creating a Ubuntu 16.04.0-LTS Standard DS2 V2 virtual machine w/ a public IP'
+ vm_create_params = ComputeModels::VirtualMachine.new.tap do |vm|
+ vm.location = location
+ vm.os_profile = ComputeModels::OSProfile.new.tap do |os_profile|
+ os_profile.computer_name = vm_name
+ os_profile.admin_username = 'notAdmin'
+ os_profile.admin_password = 'Pa$$w0rd92'
+ end
+
+ vm.storage_profile = ComputeModels::StorageProfile.new.tap do |store_profile|
+ store_profile.image_reference = ComputeModels::ImageReference.new.tap do |ref|
+ ref.publisher = 'canonical'
+ ref.offer = 'UbuntuServer'
+ ref.sku = '16.04.0-LTS'
+ ref.version = 'latest'
+ end
+ store_profile.os_disk = ComputeModels::OSDisk.new.tap do |os_disk|
+ os_disk.name = "sample-os-disk-#{vm_name}"
+ os_disk.caching = ComputeModels::CachingTypes::None
+ os_disk.create_option = ComputeModels::DiskCreateOptionTypes::FromImage
+ os_disk.vhd = ComputeModels::VirtualHardDisk.new.tap do |vhd|
+ vhd.uri = "https://#{storage_acct.name}.blob.core.windows.net/rubycontainer/#{vm_name}.vhd"
+ end
+ end
+ end
+
+ vm.hardware_profile = ComputeModels::HardwareProfile.new.tap do |hardware|
+ hardware.vm_size = ComputeModels::VirtualMachineSizeTypes::StandardDS2V2
+ end
+
+ vm.network_profile = ComputeModels::NetworkProfile.new.tap do |net_profile|
+ net_profile.network_interfaces = [
+ ComputeModels::NetworkInterfaceReference.new.tap do |ref|
+ ref.id = nic.id
+ ref.primary = true
+ end
+ ]
+ end
+
+ # Use System Assigned Identity for the VM
+ vm.identity = ComputeModels::VirtualMachineIdentity.new.tap do |identity|
+ identity.type = ComputeModels::ResourceIdentityType::SystemAssigned
+ end
+ end
+
+ ssh_pub_location = File.expand_path('~/.ssh/id_rsa.pub')
+ if File.exists? ssh_pub_location
+ puts "Found SSH public key in #{ssh_pub_location}. Disabling password and enabling SSH authentication."
+ key_data = File.read(ssh_pub_location)
+ puts "Using public key: #{key_data}"
+ vm_create_params.os_profile.linux_configuration = ComputeModels::LinuxConfiguration.new.tap do |linux|
+ linux.disable_password_authentication = true
+ linux.ssh = ComputeModels::SshConfiguration.new.tap do |ssh_config|
+ ssh_config.public_keys = [
+ ComputeModels::SshPublicKey.new.tap do |pub_key|
+ pub_key.key_data = key_data
+ pub_key.path = '/home/notAdmin/.ssh/authorized_keys'
+ end
+ ]
+ end
+ end
+ end
+
+ print_item vm = compute_client.virtual_machines.create_or_update(GROUP_NAME, "sample-ruby-vm-#{vm_name}", vm_create_params)
+
+ puts "Install Managed Service Identity Extension"
+ ext_name = 'msiextension'
+ vm_extension = ComputeModels::VirtualMachineExtension.new.tap do |extension|
+ extension.publisher = 'Microsoft.ManagedIdentity'
+ extension.virtual_machine_extension_type = 'ManagedIdentityExtensionForLinux'
+ extension.type_handler_version = '1.0'
+ extension.auto_upgrade_minor_version = true
+ extension.settings = Hash.new.tap do |settings|
+ settings['port'] = '50342'
+ end
+ extension.location = WEST_US
+ end
+
+ compute_client.virtual_machine_extensions.create_or_update(GROUP_NAME, "sample-ruby-vm-#{vm_name}", ext_name, vm_extension)
+
+ compute_client.virtual_machines.get(GROUP_NAME, "sample-ruby-vm-#{vm_name}")
+end
+
+if $0 == __FILE__
+ run_example
+end