---
title: "Introduction to AzureRMR"
author: Hong Ooi
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Introduction to AzureRMR}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{utf8}
---

AzureRMR is a package for interacting with Azure Resource Manager: authenticate, list subscriptions, manage resource groups, deploy and delete templates and resources. It calls the Resource Manager [REST API](https://learn.microsoft.com/en-us/rest/api/resources) directly, so you don't need to have PowerShell or Python installed.

As a general-purpose interface to Azure Resource Manager (ARM), you can use AzureRMR to work with nearly any Azure object that ARM can handle: subscriptions, resource groups, resources, templates and so on. The things you can do include:

- Create a new resource
- Carry out arbitrary operations on a resource
- Delete a resource
- Deploy a template
- Delete a template, and, optionally, any resources that it created
- Create and delete resource groups (if you gave your service principal subscription-level access)


## Authentication

Under the hood, AzureRMR uses a similar authentication process to the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest). The first time you authenticate with a given Azure Active Directory tenant, you call `create_azure_login()`, which will log you into Azure. R will prompt you for permission to create a special data directory in which to save your credentials. Once this information is saved on your machine, it can be retrieved in subsequent R sessions with `get_azure_login()`. Your credentials will be automatically refreshed so you don't have to reauthenticate.

Unless you have a good reason otherwise, you should allow this caching directory to be created. Note that many other cloud engineering tools save credentials in this way, including the Azure CLI itself. You can see the location of the caching directory with the function `AzureR_dir()`.

```r
library(AzureRMR)
#> The AzureR packages can save your authentication credentials in the directory:
#>
#> C:\Users\hongooi\AppData\Local\AzureR
#>
#> This saves you having to reauthenticate with Azure in future sessions. Create this directory? (Y/n) y

AzureR_dir()
#> [1] "C:\\Users\\hongooi\\AppData\\Local\\AzureR"


# if this is the first time you're logging in
az <- create_azure_login()
#> Creating Azure Resource Manager login for default tenant
#> Waiting for authentication in browser...
#> Press Esc/Ctrl + C to abort
#> Authentication complete.


# for subsequent sessions
az <- get_azure_login()
#> Loading Azure Resource Manager login for default tenant


# you can also list the tenants that you've previously authenticated with
list_azure_logins()
```

See the "Authentication basics" vignette for more details on how to authenticate with AzureRMR.


## Subscriptions and resource groups

AzureRMR allows you to work with your subscriptions and resource groups. Note that if you created your service principal via the cloud shell, as described in this vignette, you probably only have access to one subscription. Regardless, you can list all subscriptions that you can work with:

```r
# all subscriptions
az$list_subscriptions()
#> $`5710aa44-281f-49fe-bfa6-69e66bb55b11`
#> <Azure subscription 5710aa44-281f-49fe-bfa6-69e66bb55b11>
#>   authorization_source: RoleBased
#>   name: Visual Studio Ultimate with MSDN
#>   policies: list(locationPlacementId, quotaId, spendingLimit)
#>   state: Enabled
#> ---
#>   Methods:
#>     create_resource_group, delete_resource_group, get_provider_api_version, get_resource_group,
#>     list_locations, list_resource_groups, list_resources
#> 
#> $`e26f4a80-370f-4a77-88df-5a8d291cd2f9`
#> <Azure subscription e26f4a80-370f-4a77-88df-5a8d291cd2f9>
#>   authorization_source: RoleBased
#>   name: ADLTrainingMS
#>   policies: list(locationPlacementId, quotaId, spendingLimit)
#>   state: Enabled
#> ---
#>   Methods:
#>     create_resource_group, delete_resource_group, get_provider_api_version, get_resource_group,
#>     list_locations, list_resource_groups, list_resources
#> 
#> ...
```

Notice that AzureRMR is based on R6 classes, where methods are part of the object itself (much like objects in C++, C# and Java). Thus `list_subscriptions` is a member of the `az` object, and we call it with `az$list_subscriptions()`. R6 is used because it allows objects to have persistent state; in this case, the objects in R represent corresponding objects in Azure.

The `list_subscriptions()` call returns a list of subscription objects. You can retrieve the details for a single subscription with `get_subscription`:

```r
# get a subscription
(sub1 <- az$get_subscription("5710aa44-281f-49fe-bfa6-69e66bb55b11"))
#> <Azure subscription 5710aa44-281f-49fe-bfa6-69e66bb55b11>
#>   authorization_source: Legacy
#>   name: Visual Studio Ultimate with MSDN
#>   policies: list(locationPlacementId, quotaId, spendingLimit)
#>   state: Enabled
#> ---
#>   Methods:
#>     create_resource_group, delete_resource_group, get_provider_api_version, get_resource_group,
#>     list_locations, list_resource_groups, list_resources
```

A subscription object in turn has methods to get, create and delete resource groups (and also list all resource groups):

```r
(rg <- sub1$get_resource_group("rdev1"))
#> <Azure resource group rdev1>
#>   id: /subscriptions/5710aa44-281f-49fe-bfa6-69e66bb55b11/resourceGroups/rdev1
#>   location: australiaeast
#>   properties: list(provisioningState)
#> ---
#>   Methods:
#>     check, create_resource, delete, delete_resource, delete_template, deploy_template, get_resource,
#>     get_template, list_resources, list_templates

# create and delete a resource group
test <- sub1$create_resource_group("test_group")
test$delete(confirm=FALSE)
```

## Resources and templates

Methods for working with resources and templates are exposed as part of the `az_resource_group` class. You can retrieve an existing resource/template, create a new one, or delete an existing one. Below is a short introduction to resources; for templates, see the "Working with templates" vignette.

```r
(stor <- rg$get_resource(type="Microsoft.Storage/storageServices", name="rdevstor1"))
#> <Azure resource Microsoft.Storage/storageAccounts/rdevstor1>
#>   id: /subscriptions/5710aa44-281f-49fe-bfa6-69e66bb55b11/resourceGroups/rdev1/providers/Microsoft.Sto ...
#>   is_synced: TRUE
#>   kind: Storage
#>   location: australiasoutheast
#>   properties: list(networkAcls, trustedDirectories, supportsHttpsTrafficOnly, encryption,
#>     provisioningState, creationTime, primaryEndpoints, primaryLocation, statusOfPrimary)
#>   sku: list(name, tier)
#>   tags: list()
#> ---
#>   Methods:
#>     check, delete, do_operation, set_api_version, sync_fields, update
```

One benefit of the syntax that AzureRMR uses is that _method chaining_ works. This is the OOP version of pipelines, which most R users will recognise from the tidyverse.

```r
# use method chaining to get a resource without creating a bunch of intermediaries
# same result as above
stor <- az$
    get_subscription("5710aa44-281f-49fe-bfa6-69e66bb55b11")$
    get_resource_group("rdev1")$
    get_resource(type="Microsoft.Storage/storageServices", name="rdevstor1")
```

Once we have a resource, we can do _things_ with it, via the `do_operation()` method. In this case, we have a storage account. One of the things we can do with a storage account is retrieve its access keys:

```r
stor$do_operation("listKeys", http_verb="POST")
#>  $`keys`
#>  $`keys`[[1]]
#>  $`keys`[[1]]$`keyName`
#>  [1] "key1"
#>  
#>  $`keys`[[1]]$value
#>  [1] "k0gGFi8LirKcDNe73fzwDzhZ2+4oRKzvz+6+Pfn2ZCKO/JLnpyBSpVO7btLxBXQj+j8MZatDTGZ2NXUItye/vA=="
#>  
#>  $`keys`[[1]]$permissions
#>  [1] "FULL"
#> ...
```

Here is another example. If we have a virtual machine, we can start it, execute shell commands, and then shut it down again:

```r
vm <- rg$get_resource(type="Microsoft.Compute/virtualMachines",
    name="myVirtualMachine")

vm$do_operation("start", http_verb="POST") # may take a while
vm$do_operation("runCommand",
    body=list(
        commandId="RunShellScript", # RunPowerShellScript for Windows
        script=as.list("ifconfig > /tmp/ifconfig.out")
    ),
    encode="json",
    http_verb="POST")
vm$do_operation("powerOff", http_verb="POST")
```

For the types of operations you can carry out on a resource, consult the [Azure REST API documentation](https://learn.microsoft.com/en-us/rest/api/?view=Azure).

You can also interrogate the fields of a resource object; in particular the `properties` field can contain arbitrary information about an Azure resource. For example, a storage account's properties includes the endpoint URIs, and a virtual machine's properties includes its admin login details.

```r
# file and blob storage endpoint
stor$properties$primaryEndpoints$file
stor$properties$primaryEndpoints$blob

# OS profile for a VM: includes login details
vm$properties$osProfile
```


## Common methods

The following types of functionality apply at multiple levels: resource, resource group and/or subscription.

### Tagging

Resources and resource groups can be assigned _tags_. Tags are labels that help admins to organise and categorise Azure resources. To set and unset tags, use the `set_tags` method, and to view them, use `get_tags`.

```r
rg$set_tags(comment1="hello world!", created_by="AzureRMR")

# a name-only comment
rg$set_tags(comment2)

# to unset a tag, set it to NULL
rg$set_tags(created_by=NULL)

# see the tags
rg$get_tags()
#> $comment1
#> [1] "hello world!"
#> 
#> $comment2
#> [1] ""

# specify keep_existing=FALSE to unset all existing tags
rg$set_tags(newcomment="my new comment", keep_existing=FALSE)
```

### Locking

As of version 2.0.0, AzureRMR includes the ability to lock and unlock subscriptions, resource groups and resources. The methods involved are:

* `create_lock(name, level)`: Create a management lock on this object. The `level` argument can be either "cannotdelete" or "readonly".
* `get_lock(name`): Returns a management lock object.
* `delete_lock(name)`: Deletes a management lock object.
* `list_locks()`: List all locks that apply to this object.

Locks applied to a resource group also apply to all its resources, and similarly locks applied to a subscription also apply to all its resource groups (and resources). If you logged in via a custom service principal, it must have "Owner" or "User Access Administrator" access to manage locks.

```r
# protect this resource group and its resources against deletion
rg$create_lock("mylock", "cannotdelete")

rg$list_locks()

rg$delete_lock("mylock")
```

### Role-based access control

As of version 2.1.0, AzureRMR includes limited support for [role-based access control](https://learn.microsoft.com/en-us/azure/role-based-access-control/overview) (RBAC). You can read role definitions, and read, add and remove role assignments. This applies to subscription, resource group and resource objects.

```r
rg$list_role_definitions()
#>                          definition_id                                                name
#> 1 a7b1b19a-0e83-4fe5-935c-faaefbfd18c3                                Avere Cluster Create
#> 2 e078ab98-ef3a-4c9a-aba7-12f5172b45d0                      Avere Cluster Runtime Operator
#> 3 21d96096-b162-414a-8302-d8354f9d91b2 Azure Service Deploy Release Management Contributor
#> 4 7b266cd7-0bba-4ae2-8423-90ede5e1e898                                     CAL-Custom-Role
#> 5 b91f4c0b-46e3-47bb-a242-eecfe23b3b5b                              Dsms Role (deprecated)
#> ...

rg$get_role_definition("Reader")
#> <Azure role definition>
#>   role: Reader
#>   description: Lets you view everything, but not make any changes.
#>   role definition ID: acdd72a7-3385-48ef-bd42-f606fba81ae7

# assign Reader role to the principal with the given ID
rg$add_role_assignment("041ff2be-4eb0-11e9-8f38-394fbcd0b29d", "Reader")
```

You can assign roles to either a user or a service principal, although note that the ID of a service principal is _not_ the app ID of its corresponding registered app. The AzureGraph package can help you in specifying the principal to which to assign a role.

```r
gr <- AzureGraph::get_graph_login()

# can get a user by their email address
usr <- gr$get_user("username@aadtenant.com")

# get the service principal for an app by its app ID
svc <- gr$get_service_principal(app_id="b9ed4812-4eba-11e9-9a1e-1fda262d9c77")

rg$add_role_assignment(usr, "Reader")
rg$add_role_assignment(svc, "Contributor")
```

Technically role definitions and assignments are _resources_ and could be manipulated as such, but AzureRMR currently treats them as independent classes.


## Conclusion

This has been a quick introduction to the features of AzureRMR. Remember that this package is only meant to be a generic mechanism for working with Resource Manager. You can extend it to provide support for service-specific features; examples of packages that do this include [AzureVM](https://github.com/cloudyr/AzureVM) for [virtual machines](https://azure.microsoft.com/en-us/services/virtual-machines/), and [AzureStor](https://github.com/cloudyr/AzureStor) for [storage accounts](https://azure.microsoft.com/en-us/product-categories/storage/). For more information, see the "Extending AzureRMR" vignette.
