# Install an Azure connector on ACI using PowerShell

Azure Container Instances (ACI) is a managed, serverless compute platform for running containerized applications. This guide explains how to install and configure an Apono connector on ACI in your Azure environment using PowerShell.

***

### Prerequisites

<table><thead><tr><th width="249">Item</th><th>Description</th></tr></thead><tbody><tr><td><strong>Apono Token</strong></td><td><p>Account-specific Apono authentication value</p><p>Use the following steps to obtain your token:</p><ol><li>On the <a href="https://app.apono.io/connectors"><strong>Connectors</strong></a> page, click <strong>Install Connector</strong>. The <strong>Install Connector</strong> page appears.</li><li>Click <strong>Cloud installation > Azure > Install and Connect Azure Account > CLI (Container Instance)</strong>.</li><li>Copy the token listed on the page in step <strong>1</strong>.</li></ol></td></tr><tr><td><strong>PowerShell</strong></td><td><a href="https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7.4">Tool</a> that enables interacting with Azure services using your command-line shell</td></tr><tr><td><strong>Azure Cloud Information</strong></td><td><p>Information for your Azure Cloud instance:</p><ul><li><a href="https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id">Subscription ID</a></li><li><a href="https://learn.microsoft.com/en-us/azure/governance/management-groups/manage#view-management-groups">Management Group Name</a></li><li><a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal#open-resource-groups">Resource group name</a></li></ul></td></tr><tr><td><strong>Owner Role (Azure RBAC)</strong></td><td><p><a href="https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#owner">Azure role</a> with the following permissions:</p><ul><li>Grants full access to manage all resources</li><li>Assigns roles in Azure RBAC</li></ul></td></tr><tr><td><strong>Global Administrator</strong></td><td><p><a href="https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#global-administrator">Microsoft Entra role</a> with the following permission:</p><ul><li>Manages all aspects of Microsoft Entra ID and Microsoft services that use Microsoft Entra identities</li></ul><p>❗<strong>Apono does not require Global Administrator access. This is required for the admin following this guide.</strong> ❗</p></td></tr></tbody></table>

***

### Install a new connector

You can install a connector for an Azure **Management Group** or **Subscription.**

{% hint style="info" %}
The connector requires the following roles:

1. Directory Readers - to validate users in Azure
2. User Access Administrator - to provision and deprovision access in the Management Group

Read more about these Microsoft Entra ID roles [here](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#directory-readers).
{% endhint %}

{% tabs %}
{% tab title="Management Group" %}
Follow these steps to install a new connector:

1. At the shell prompt, set the environment variables.

```powershell
$env:APONO_CONNECTOR_ID = "<A_UNIQUE_CONNECTOR_NAME>"
$env:APONO_TOKEN = "<APONO_TOKEN>"
$env:SUBSCRIPTION_ID = "<AZURE_SUBSCRIPTION_ID>"
$env:RESOURCE_GROUP_NAME = "<AZURE_RESOURCE_GROUP_NAME>"
$env:MANAGEMENT_GROUP_NAME = "<AZURE_MANAGEMENT_GROUP_NAME>"
```

2. Log in to your Azure account.

```powershell
Connect-AzAccount
```

3. Set the `REGION` environment variable.

{% code overflow="wrap" %}

```powershell
$REGION=$(Get-AzResourceGroup -Name $RESOURCE_GROUP_NAME).Location
```

{% endcode %}

4. Run the following command to deploy the connector on your ACI.

{% code overflow="wrap" %}

```powershell
$port = New-AzContainerInstancePortObject -Port 80 -Protocol TCP

$env_var1 = New-AzContainerInstanceEnvironmentVariableObject -Name "APONO_CONNECTOR_ID" -Value $APONO_CONNECTOR_ID

$env_var2 = New-AzContainerInstanceEnvironmentVariableObject -Name "APONO_TOKEN" -Value $APONO_TOKEN

$env_var3 = New-AzContainerInstanceEnvironmentVariableObject -Name "APONO_URL" -Value "api.apono.io"

$jsonValue = @{
    cloud_provider = "AZURE"
    subscription_id = $SUBSCRIPTION_ID
    resource_group = $RESOURCE_GROUP_NAME
    region = $REGION
    is_azure_admin = $true
} | ConvertTo-Json -Compress

$env_var4 = New-AzContainerInstanceEnvironmentVariableObject -Name "CONNECTOR_METADATA" -Value $jsonValue

$container = New-AzContainerInstanceObject -Image registry.apono.io/apono-connector:v1.7.8 -Name $APONO_CONNECTOR_ID -Port @($port) -EnvironmentVariable @($env_var1, $env_var2, $env_var3, $env_var4) -RequestCpu 1 -RequestMemoryInGb 2 

$imageRegistryCredential = New-AzContainerGroupImageRegistryCredentialObject -Server "registry.apono.io" -Username "apono" -Password (ConvertTo-SecureString $APONO_TOKEN -AsPlainText -Force)

$PRINCIPAL_ID=$(New-AzContainerGroup -SubscriptionId $SUBSCRIPTION_ID -ResourceGroupName $RESOURCE_GROUP_NAME -Name $APONO_CONNECTOR_ID -Container $container -OsType Linux -ImageRegistryCredential $imageRegistryCredential -Location $REGION -IdentityType "SystemAssigned").IdentityPrincipalId
```

{% endcode %}

5. Add the **User Access Administrator** role to the connector in the management group scope.

{% code overflow="wrap" %}

```powershell
New-AzRoleAssignment -ObjectId $PRINCIPAL_ID `
    -ObjectType "ServicePrincipal" `
    -RoleDefinitionName "User Access Administrator" `
    -Scope "/providers/Microsoft.Management/managementGroups/$env:MANAGEMENT_GROUP_NAME"
```

{% endcode %}

6. If your Azure resources have [resource locks](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources) applied, assign the **Tag Contributor** role to the connector at the management scope. This allows Apono to add a tag marker during the grant or revoke process.

{% code overflow="wrap" %}

```powershell
New-AzRoleAssignment -ObjectId $PRINCIPAL_ID `
    -ObjectType "ServicePrincipal" `
    -RoleDefinitionName "Tag Contributor" `
    -Scope "/providers/Microsoft.Management/managementGroups/$env:MANAGEMENT_GROUP_NAME"
```

{% endcode %}

7. For Azure AD, add the **Directory Readers** role to the connector. For Azure AD Groups, add the **Groups Administrator** and **Privileged Role Administrator** roles.

{% tabs %}
{% tab title="Azure AD" %}
{% code overflow="wrap" %}

```powershell
$accessToken = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token

$payload = @{
    principalId = $PRINCIPAL_ID
    roleDefinitionId = "88d8e3e3-8f55-4a1e-953a-9b9898b8876b"
    directoryScopeId = "/"
} | ConvertTo-Json -Depth 3

$headers = @{
    "Authorization" = "Bearer $accessToken"
    "Content-Type"  = "application/json"
}

Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments" -Headers $headers -Body $payload
```

{% endcode %}
{% endtab %}

{% tab title="Azure AD Groups" %}
{% code overflow="wrap" %}

```powershell
$accessToken = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token

$headers = @{
    "Authorization" = "Bearer $accessToken"
    "Content-Type"  = "application/json"
}

$payload1 = @{
    principalId       = $PRINCIPAL_ID
    roleDefinitionId  = "fdd7a751-b60b-444a-984c-02652fe8fa1c"  # Role ID 1
    directoryScopeId  = "/"
} | ConvertTo-Json -Depth 3

Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments" -Headers $headers -Body $payload1

$payload2 = @{
    principalId       = $PRINCIPAL_ID
    roleDefinitionId  = "e8611ab8-c189-46e8-94e1-60213ab1f814"  # Role ID 2
    directoryScopeId  = "/"
} | ConvertTo-Json -Depth 3

Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments" -Headers $headers -Body $payload2
```

{% endcode %}
{% endtab %}
{% endtabs %}

8. On the [**Connectors**](https://app.apono.io/connectors) page, verify that the connector has been updated.

You can now integrate with an [Azure Management Group or Azure Subscription](https://docs.apono.io/docs/azure-environment/azure-integrations/integrate-with-azure-management-groups-or-subscriptions).
{% endtab %}

{% tab title="Subscription" %}
Follow these steps to install a new connector:

1. At the shell prompt, set the environment variables.

```powershell
$env:APONO_CONNECTOR_ID = "<A_UNIQUE_CONNECTOR_NAME>"
$env:APONO_TOKEN = "<APONO_TOKEN>"
$env:SUBSCRIPTION_ID = "<AZURE_SUBSCRIPTION_ID>"
$env:RESOURCE_GROUP_NAME = "<AZURE_RESOURCE_GROUP_NAME>"
```

2. Log in to your Azure account.

```powershell
Connect-AzAccount
```

3. Set the `REGION` environment variable.

{% code overflow="wrap" %}

```powershell
$env:REGION=$(Get-AzResourceGroup -Name $env:RESOURCE_GROUP_NAME).Location
```

{% endcode %}

4. Run the following command to deploy the connector on your ACI.

{% code overflow="wrap" %}

```powershell
$port = New-AzContainerInstancePortObject -Port 80 -Protocol TCP

$env_var1 = New-AzContainerInstanceEnvironmentVariableObject -Name "APONO_CONNECTOR_ID" -Value $env:APONO_CONNECTOR_ID

$env_var2 = New-AzContainerInstanceEnvironmentVariableObject -Name "APONO_TOKEN" -Value $env:APONO_TOKEN

$env_var3 = New-AzContainerInstanceEnvironmentVariableObject -Name "APONO_URL" -Value "api.apono.io"

$jsonValue = @{
    cloud_provider = "AZURE"
    subscription_id = $env:SUBSCRIPTION_ID
    resource_group = $env:RESOURCE_GROUP_NAME
    region = $env:REGION
    is_azure_admin = $true
} | ConvertTo-Json -Compress

$env_var4 = New-AzContainerInstanceEnvironmentVariableObject -Name "CONNECTOR_METADATA" -Value $jsonValue

$container = New-AzContainerInstanceObject -Image registry.apono.io/apono-connector:v1.7.6 -Name $env:APONO_CONNECTOR_ID -Port @($port) -EnvironmentVariable @($env_var1, $env_var2, $env_var3, $env_var4) -RequestCpu 1 -RequestMemoryInGb 2

$imageRegistryCredential = New-AzContainerGroupImageRegistryCredentialObject -Server "registry.apono.io" -Username "apono" -Password (ConvertTo-SecureString $env:APONO_TOKEN -AsPlainText -Force)

$PRINCIPAL_ID=$(New-AzContainerGroup -SubscriptionId $env:SUBSCRIPTION_ID -ResourceGroupName $env:RESOURCE_GROUP_NAME -Name $env:APONO_CONNECTOR_ID -Container $container -OsType Linux -ImageRegistryCredential $imageRegistryCredential -Location $env:REGION -IdentityType "SystemAssigned").IdentityPrincipalId
```

{% endcode %}

5. Add the **User Access Administrator** role to the connector in the subscription scope.

{% code overflow="wrap" %}

```powershell
New-AzRoleAssignment -ObjectId $PRINCIPAL_ID `
    -ObjectType "ServicePrincipal" `
    -RoleDefinitionName "User Access Administrator" `
    -Scope "/subscriptions/$env:SUBSCRIPTION_ID"
```

{% endcode %}

6. If your Azure resources have [resource locks](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/lock-resources) applied, assign the **Tag Contributor** role to the connector at the subscription scope. This allows Apono to add a tag marker during the grant or revoke process.

```powershell
New-AzRoleAssignment -ObjectId $PRINCIPAL_ID `
    -ObjectType "ServicePrincipal" `
    -RoleDefinitionName "Tag Contributor" `
    -Scope "/subscriptions/$env:SUBSCRIPTION_ID"
```

7. For Azure AD, add the **Director Readers** role to the connector. For Azure AD Groups, add the **Groups Administrator** and **Privileged Role Administrator** roles.

{% tabs %}
{% tab title="Azure AD" %}
{% code overflow="wrap" %}

```powershell
$accessToken = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token

$payload = @{
    principalId = $PRINCIPAL_ID
    roleDefinitionId = "88d8e3e3-8f55-4a1e-953a-9b9898b8876b"
    directoryScopeId = "/"
} | ConvertTo-Json -Depth 3

$headers = @{
    "Authorization" = "Bearer $accessToken"
    "Content-Type"  = "application/json"
}

Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments" -Headers $headers -Body $payload
```

{% endcode %}
{% endtab %}

{% tab title="Azure AD Groups" %}
{% code overflow="wrap" %}

```powershell
$accessToken = (Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com").Token

$headers = @{
    "Authorization" = "Bearer $accessToken"
    "Content-Type"  = "application/json"
}

$payload1 = @{
    principalId       = $PRINCIPAL_ID
    roleDefinitionId  = "fdd7a751-b60b-444a-984c-02652fe8fa1c"  # Role ID 1
    directoryScopeId  = "/"
} | ConvertTo-Json -Depth 3

Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments" -Headers $headers -Body $payload1

$payload2 = @{
    principalId       = $PRINCIPAL_ID
    roleDefinitionId  = "e8611ab8-c189-46e8-94e1-60213ab1f814"  # Role ID 2
    directoryScopeId  = "/"
} | ConvertTo-Json -Depth 3

Invoke-RestMethod -Method POST -Uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments" -Headers $headers -Body $payload2
```

{% endcode %}
{% endtab %}
{% endtabs %}

8. On the [**Connectors**](https://app.apono.io/connectors) page, verify that the connector has been updated.

You can now create integrate with an [Azure Management Group or Azure Subscription](https://docs.apono.io/docs/azure-environment/azure-integrations/integrate-with-azure-management-groups-or-subscriptions).
{% endtab %}
{% endtabs %}
