Azure PowerShell cmdlet Set-AzRouteConfig - azure

I am trying to find out how Azure PowerShell cmdlet Set-AzRouteConfig works under the hood , what calls it makes to Azure APIs etc. The goal is to achieve a similar functionality using Azure SDK for Python.

For the set-AzRouteConfig operations to be performed in Azure SDK for python , It needs az-mgmt-network package so that it can use NetworkManagementClient Class to perform 2 operations i.e. RouteTableOperations and RouteOperations . For the updating the route present in a route_table you can use begin_create_or_update.
Example:
Code:
from azure.identity import AzureCliCredential
from azure.mgmt.network import NetworkManagementClient
from azure.mgmt.network.models import Route
credential = AzureCliCredential()
subscription_id = "b83c1ed3-xxxx-xxxx-xxxx-2b83a074c23f"
network_client = NetworkManagementClient(credential, subscription_id)
resource_group = "ansumantest"
location = "West US 2"
route_table_name = "ansuman-routetable"
route_name = "route1"
get_table=network_client.route_tables.get(resource_group,route_table_name)
print(get_table)
route_parameters= Route(name='route1',address_prefix='10.0.0.0/32',next_hop_type='VnetLocal')
set_route_config= network_client.routes.begin_create_or_update(resource_group,route_table_name,route_name,route_parameters)
get_route_config= network_client.routes.get(resource_group,route_table_name,route_name)
print(get_route_config)
Output:
Before Updating the route :
After updating the route :
Properties changed are address_prefix and next_hop_type.

Related

How do I add a new API version via Terraform in Azure API Manager?

I would like to add a new API version under the existing API that is already there. I can do that easily via the Portal UI however, can someone please guide me to how to achieve this via Terraform? Any sample snippet would be helpful.
I am trying to reverse engineer v1, v2 of this into Terraform. Thanks.
Terraform supports API Version Set and reference it in the API:
version - (Optional) The Version number of this API, if this API is versioned.
version_set_id - (Optional) The ID of the Version Set which this API is associated with.
NOTE:
When version is set, version_set_id must also be specified
resource "azurerm_api_management_api_version_set" "example" {
name = "example-apimapi"
resource_group_name = var.resource_group_name
api_management_name = var.apim_name
display_name = "ExampleAPIVersionSet"
versioning_scheme = "Segment"
}
resource "azurerm_api_management_api" "example" {
name = "example-api"
resource_group_name = var.resource_group_name
api_management_name = var.apim_name
revision = "1"
display_name = "Example API"
path = "example_me"
protocols = ["https"]
service_url = "https://conferenceapi.azurewebsites.net/"
version = "v1"
version_set_id = azurerm_api_management_api_version_set.example.id
import {
content_format = "swagger-link-json"
content_value = "http://conferenceapi.azurewebsites.net/?format=json"
}
}
There's a tutorial for doing it the Portal: Tutorial: Publish multiple versions of your API
When you create multiple versions, the Azure portal creates a version set, which represents a set of versions for a single logical API. Select the name of an API that has multiple versions. The Azure portal displays its Version set. You can customize the Name and Description of a virtual set.

terraform backend state file storage using keys instead of AD account

It appears that Terraform uses Keys for backend state files when persisting to an Azure storage account. I wish to use a single storage account with dedicated folders for different service principals but without cross-folder write access. I am trying to avoid accidental overwrites of the state files by different service principals. But since Terraform is using the keys to update the storage account, every service principal technically has rights to update every file. And the developer would have to take care not to accidentally reference the wrong state file to update. Any thoughts on how to protect against this?
You can use a SAS token generated for a Container to be used by that service principal only and no other service principals .
I tested with something like below:
data "terraform_remote_state" "foo" {
backend = "azurerm"
config = {
storage_account_name = "cloudshellansuman"
container_name = "test"
key = "prod.terraform.tfstate"
sas_token = "sp=racwdl&st=2021-09-28T05:49:01Z&se=2023-04-01T13:49:01Z&sv=2020-08-04&sr=c&sig=O87nHO01sPxxxxxxxxxxxxxsyQGQGLSYzlp6F8%3D"
}
}
provider "azurerm" {
features {}
use_msi = true
subscription_id = "948d4068-xxxxx-xxxxxx-xxxxxxxxxx"
tenant_id = "72f988bf-xxxx-xxxxx-xxxxxx-xxxxxxx"
}
resource "azurerm_resource_group" "test" {
name="xterraformtest12345"
location ="east us"
}
But If I change container name to another container then I can't write as it will error out saying the authentication failed as the SAS token is for Test container not Test1 container.
For more information on how to generate SAS token for containers and how to set backend azurerm for terraform , please refer the below links:
Generate shared access signature (SAS) token for containers and blobs with Azure portal. | Microsoft Docs
Use Azure storage for Terraform remote state
OR
You can set the containers authentication method to azure ad user account, after assigning storage blob data contributor/owner role to the service principal which will use that specific container .
Then you can use something like below:
data "terraform_remote_state" "foo" {
backend = "azurerm"
config = {
storage_account_name = "cloudshellansuman"
container_name = "test1"
key = "prod.terraform.tfstate"
subscription_id = "b83c1ed3-xxxx-xxxxxx-xxxxxxx"
tenant_id = "72f988bf-xxx-xxx-xxx-xxx-xxxxxx"
client_id = "f6a2f33d-xxxx-xxxx-xxx-xxxxx"
client_secret = "y5L7Q~oiMOoGCxm7fK~xxxxxxxxxxxxxxxxx"
use_azuread_auth =true
}
}
provider "azurerm"{
subscription_id = "b83c1ed3-xxxx-xxxxxx-xxxxxxx"
tenant_id = "72f988bf-xxx-xxx-xxx-xxx-xxxxxx"
client_id = "f6a2f33d-xxxx-xxxx-xxx-xxxxx"
client_secret = "y5L7Q~oiMOoGCxm7fK~xxxxxxxxxxxxxxxxx"
features {}
}
data "azurerm_resource_group" "test" {
name="resourcegroupname"
}
resource "azurerm_virtual_network" "example" {
name = "example-network"
resource_group_name = data.azurerm_resource_group.test.name
location = data.azurerm_resource_group.test.location
address_space = ["10.254.0.0/16"]
}
Output:
If service principal doesn't have Role assigned to it for the container , then it will give error like below:
Note: For the first scenario I have used managed system identity, but the same can be achieved for service principal as well.

How to edit Azure App Service using Azure Python SDK?

I'm following the example here https://learn.microsoft.com/en-us/azure/developer/python/azure-sdk-example-web-app?tabs=cmd#4-write-code-to-provision-and-deploy-a-web-app trying to update an existing App Service using the Python SDK.
Here is my code
from azure.mgmt.web import WebSiteManagementClient
from azure.common.client_factory import get_client_from_cli_profile
import os
def insert_access_restriction():
rg_name = os.environ.get('RESOURCE_GROUP_NAME', None)
location = os.environ.get('LOCATION', None)
sp_name = os.environ.get('SERVICE_PLAN_NAME', None)
web_app_name = os.environ.get('WEB_APP_NAME', None)
sub_id = os.environ.get('AZURE_SUBSCRIPTION_ID', None)
app_service_client = get_client_from_cli_profile(WebSiteManagementClient)
poller = app_service_client.app_service_plans.create_or_update(rg_name,
sp_name,
{
"location": location,
"reserved": True,
"sku" : {"name" : "S1"}
}
)
plan_result = poller.result()
poller = app_service_client.web_apps.create_or_update(rg_name,
web_app_name,
{
"location": location,
"server_farm_id": plan_result.id,
"site_config": {
"ip_restriction": {
"ip_address": "3.3.3.3/32"
},
"ip_restriction": {
"ip_address": "4.4.4.4/32"
}
}
}
)
The call to this function app_service_client.app_service_plans.create_or_update returns
azure.mgmt.web.v2019_08_01.models._models_py3.DefaultErrorResponseException:
Operation returned an invalid status code 'Bad Request'
My location is centralus. The goal of this program is to update the ip restrictions on an existing app service programmatically from a Function App when a new list of ip addresses is added to a storage container. The error is very vague, how do I get an existing app service plan, its app service, and then update the app service using the Python SDK?
if you want to update the ip restrictions settings on an existing app service with python sdk, please refer to the following code
Create a service principal and assign Contributor to the sp
az login
# create sp and assign Contributor role to the sp at subscription level
az ad sp create-for-rbac -n "MyApp"
Code
client_id = 'your sp appId'
secret = 'your sp password'
tenant = 'your sp tenant'
credentials = ServicePrincipalCredentials(
client_id = client_id,
secret = secret,
tenant = tenant
)
Subscription_Id = ''
web_client=WebSiteManagementClient(
credentials,
Subscription_Id
)
resorurce_group_name='your appservice group name'
name='you appservice name'
web_client.web_apps.create_or_update_configuration(resorurce_group_name, name,{
'ip_security_restrictions':[
{
'ip_address': "0.0.0.0/0",
'action': "Allow",
'priority': 30,
'name': "test"
}
]
})
for more details, please refer to here and here
#Update
If you want to run the script in Azure function, please refer to the following steps.
Create Azure function
Enable Azure MSI for Azure Function
Assign role fro the MSI
code (I use HTTP trigger to get web app configuration)
import logging
import pyodbc
import json
import azure.functions as func
from msrestazure.azure_active_directory import MSIAuthentication
from azure.mgmt.web import WebSiteManagementClient
async def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
creds =MSIAuthentication()
Subscription_Id = 'e5b0fcfa-e859-43f3-8d84-5e5fe29f4c68'
group_name='0730BowmanWindowAndLinux2'
name='413Bowman'
web_client=WebSiteManagementClient(
creds,
Subscription_Id
)
result =web_client.web_apps.get_configuration(group_name, name,raw=True)
return func.HttpResponse(json.dumps(result.response.json()))

Issues with importing resource to terraform module

I have created module named workflow for Azure LogicApp
Here is the module:
resource "azurerm_logic_app_workflow" "LogicApp" {
name = "${var.LogicAppName}"
location = "${var.LogicAppLocation}"
resource_group_name = "${var.rgName}"
workflow_schema = "${var.schema}"
}
In workflow_schema i'm specifying the path to my file which contains the logicapp configuration
In main config.tf I have the following setup:
module "workflow" {
source = "./modules/workflow/"
LogicAppName = "LaName"
LogicAppLocation = "${azurerm_resource_group.rg.location}"
rgName = "${azurerm_resource_group.rg.name}"
schema = "${file("./path/to/the/file/LaName")}"
}
So, when I'm running terraform init and terraform plan everything works perfectly fine.
Since my logic app was created earlier, I want to import it so that terraform apply won't overwrite it.
I am running the following command and it returns the error:
terraform import module.workflow.azurerm_logic_app_workflow.LogicApp /subscriptions/mySubscriptionID/resourceGroups/myRgName/providers/Microsoft.Logic/workflows/LaName
Error: Import to non-existent module
module.workflow is not defined in the configuration. Please add configuration
for this module before importing into it.
I'm using the following versions of software:
Terraform v0.12.13
+ provider.azurerm v1.28.0
If anyone has any ideas why terraform import fails, please share them.
I see the issue in naming.
Your module is named workflow and in your configuration you name the resource workflow too, this should be different. You are trying to import into the resource directly.
Example:
module "workflow-azure" {
source = "./modules/workflow/"
LogicAppName = "LaName"
LogicAppLocation = "${azurerm_resource_group.rg.location}"
rgName = "${azurerm_resource_group.rg.name}"
schema = "${file("./path/to/the/file/LaName")}"
}
and the import should be then
terraform import module.workflow-azure.azurerm_logic_app_workflow.LogicApp /subscriptions/mySubscriptionID/resourceGroups/myRgName/providers/Microsoft.Logic/workflows/LaName

How can I use Terraform to create a service principal and use that principal in a provider?

I have read the write-ups online but they dont seem to cover this topic completely and was hoping someone who has done it may have some direction for me.
We are setting up a complicated Terraform template to satisfy our IaC requirements relating to our SaaS offering. In doing so we want the template to use the user's credentials at launch to create a new service principal in Azure AD (This part I have no problem doing). Then in the next portion of the template we are using that service principal as the provider. Problem is that it throws errors in the plan/apply because the service principal doesnt exist (aka the id is non existent due to the service provider section not running yet).
So is there a way that I can do this? Create a service principal and then us it in a provider alias that uses that service principal without splitting this into multiple templates?
In the end I want this template to create the service provider using the local user's permissions or MSI, give it RBAC to a subscription, then use that service provider to create assets in that subscription.
main.ts (root)
provider "azurerm" {
alias = "ActiveDirectory"
subscription_id = "${var.subscriptionNucleus}"
}
provider "azurerm" {
alias = "Infrastructure"
subscription_id = "${var.subscriptionInfrastructure}"
}
module "activedirectory" {
providers = { azurerm = "azurerm.ActiveDirectory"
}
source = "./modules/activedirectory"
subscription_id_infrastructure = "${var.subscriptionInfrastructure}"
}
module "infrastructure" {
providers = { azurerm = "azurerm.Infrastructure"}
source = "./modules/infrastructure"
location = "${var.location}"
application_id =
"${module.activedirectory.service_principal_application_id}"
subscription_id = "${var.subscriptionInfrastructure}"
prefix = "${var.prefix}"
}
main.ts (./modules/infrastructure)
data "azurerm_azuread_service_principal" "serviceprincipal" {
application_id = "${var.application_id}"
}
provider "azurerm" {
alias = "InfrastructureSP"
subscription_id = "${var.subscription_id}"
client_id = "${var.application_id}"
client_secret = "secret"
tenant_id =
"${data.azurerm_client_config.clientconfig.tenant_id}"
}
For Azure Service Principal, there are two ways to use the service principal.
First: If you already have a service principal and want to use it in the Terraform. You can make use of the Terraform Data and the test like this:
data "azurerm_azuread_service_principal" "sp" {
application_id = "21f3e1de-54e2-4951-9743-c280ad7bd74a"
}
output "test" {
value = "${data.azurerm_azuread_service_principal.sp.id}"
}
The screenshot of the result is here:
Second: You don't have the service principal and you can just create a service principal in the Terraform like this:
resource "azurerm_azuread_service_principal" "test" {
application_id = "${azurerm_azuread_application.test.application_id}"
}
resource "azurerm_azuread_service_principal_password" "test" {
service_principal_id = "${azurerm_azuread_service_principal.test.id}"
value = "your pasword"
end_date = "2020-01-01T01:02:03Z"
}
Then, no matter which way you choose, there is an important step you should do for most resources. The step is that you need to create the role to give the permission and then assign it to the resource which needs. You can do that like this:
resource "azurerm_role_assignment" "test" {
scope = "yourScope" # the resource id
role_definition_name = "the Role In need" # such as "Contributor"
principal_id = "your service principal id"
}
Hope this will help you.
There is currently no working "depends_on" that works with modules that is not a hack (null_reference). This means that if you are breaking your template into modules(separating concerns) your order of operation is required to be correct to complete this successfully as one module will not know that the data source of service provider has to wait on the previous module to complete. I have had to break this into 2 separate templates where the first creates the service principal and the second has the modular separation that can then use a data source of azurerm_azuread_service_principal.
Once Hashicorp can implement the module depends_on, this will become easier.

Resources