I am working on deploy resources in Azure using python based on provided templates. As a starting point I am working with https://github.com/Azure-Samples/Hybrid-Resource-Manager-Python-Template-Deployment
Using as it is, I am having an issue at the beginning of the deployment (deployer.py deploy function)
def deploy(self, template, parameters):
"""Deploy the template to a resource group."""
self.client.resource_groups.create_or_update(
self.resourceGroup,
{
'location': os.environ['AZURE_RESOURCE_LOCATION']
}
)
The error message is
Message='ServicePrincipalCredentials' object has no attribute
'get_token'
The statement is correct, ServicePrincipalCredentials get_token attribute doesn't exist, however token is, may be an error due to an outdated version?
Based on the constructor information, the error may be on credentials creation or client creation
def __init__(self, subscription_id, resource_group, pub_ssh_key_path='~/id_rsa.pub'):
mystack_cloud = get_cloud_from_metadata_endpoint(
os.environ['ARM_ENDPOINT'])
subscription_id = os.environ['AZURE_SUBSCRIPTION_ID'] //This may be an error as subscription_id is already provided as a parameter
credentials = ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID'],
cloud_environment=mystack_cloud
) --> here
self.subscription_id = subscription_id
self.resource_group = resource_group
self.dns_label_prefix = self.name_generator.haikunate()
pub_ssh_key_path = os.path.expanduser(pub_ssh_key_path)
# Will raise if file not exists or not enough permission
with open(pub_ssh_key_path, 'r') as pub_ssh_file_fd:
self.pub_ssh_key = pub_ssh_file_fd.read()
self.credentials = credentials
self.client = ResourceManagementClient(self.credentials, self.subscription_id,
base_url=mystack_cloud.endpoints.resource_manager) --> here
Do you know how I can fix this?
After struggling a little, I could find a solution. Just replace
credentials = ServicePrincipalCredentials(
client_id=os.environ['AZURE_CLIENT_ID'],
secret=os.environ['AZURE_CLIENT_SECRET'],
tenant=os.environ['AZURE_TENANT_ID'],
cloud_environment=mystack_cloud
)
for
self.credentials = DefaultAzureCredential()
The final code looks like:
from azure.identity import DefaultAzureCredential
def __init__(self, subscriptionId, resourceGroup):
endpoints = get_cloud_from_metadata_endpoint(os.environ.get("ARM_ENDPOINT"))
self.subscriptionId = subscriptionId
self.resourceGroup = resourceGroup
self.credentials = DefaultAzureCredential()
self.client = ResourceManagementClient(self.credentials, self.subscriptionId,
base_url=endpoints.endpoints.resource_manager)
Related
I am trying to deploy resources to Azure China with ARM Templates. I had the code to do it for Azure, and now I am adapting it for Azure China, I believe that the only changes that I should perform were in
Change the authority host in the credentials
self.credentials = DefaultAzureCredential(authority = AzureAuthorityHosts.AZURE_CHINA)
Change the Management Url in the Client.
endpoints = get_cloud_from_metadata_endpoint(os.environ.get("ARM_ENDPOINT"))
self.client = ResourceManagementClient(self.credentials, self.subscriptionId, base_url=endpoints.endpoints.resource_manager)
Below is the code that I am using
Python Code:
def __init__(self, subscriptionId, resourceGroup):
self.logger = Logger("Azure China Connection")
self.logger.info("Retrieving the list of available endpoint")
# ARM_ENDPOINT = https://management.chinacloudapi.cn
endpoints = get_cloud_from_metadata_endpoint(os.environ.get("ARM_ENDPOINT"))
self.subscriptionId = subscriptionId
self.resourceGroup = resourceGroup
self.credentials = DefaultAzureCredential(authority = AzureAuthorityHosts.AZURE_CHINA)
self.logger.info("Creating a client for deploying resources on subscription {}".format(self.subscriptionId))
self.client = ResourceManagementClient(self.credentials, self.subscriptionId,
# endpoints.endpoints.resource_manager = https://management.chinacloudapi.cn
base_url=endpoints.endpoints.resource_manager)
self.logger.success("Client was successfully created")
def deploy(self, template, parameters):
resources = ""
for resource in template.get("resources"):
resources += "\n\t {}".format(resource.get("type"))
self.logger.info("The following resources: {}\nwill be deployed".format(resources))
deploymentProperties = DeploymentProperties(
mode = DeploymentMode.incremental,
template = template,
parameters = parameters.get("parameters")
)
self.logger.info("Attempting deploy operation")
deployment = self.client.deployments.begin_create_or_update(
self.resourceGroup,
uuid7(),
Deployment(properties=deploymentProperties)
) # Error occurs here
self.logger.success("Resources deployment successfully triggered")
return deployment.result()
load_dotenv()
connection = new AzureChinaConnection(os.environ.get("AZURE_SUBSCRIPTION_ID"), os.environ.get("AZURE_RESOURCE_GROUP"))
deployment = self.connection.deploy(template.json(), parameter.json())
**Message=**DefaultAzureCredential failed to retrieve a token from the
included credentials. Attempted credentials: EnvironmentCredential:
Authentication failed: AADSTS500011: The resource principal named
https://management.azure.com was not found in the tenant named EY
CHINA. This can happen if the application has not been installed by
the administrator of the tenant or consented to by any user in the
tenant. You might have sent your authentication request to the wrong
tenant. Trace ID: ace63d66-af4b-4457-b6c9-6ce050e34700 Correlation ID:
d85942a5-35fb-493f-8eef-ee9fe1f64b7f Timestamp: 2022-09-29 19:44:47Z
To mitigate this issue, please refer to the troubleshooting guidelines
here at
https://aka.ms/azsdk/python/identity/defaultazurecredential/troubleshoot.
Based on the error message, it looks like I am pointing to a wrong endpoint https://management.azure.com instead of https://management.chinacloudapi.cn. The question is, where should I set it?
I though that it was already doing it in the __init__
self.client = ResourceManagementClient(self.credentials, self.subscriptionId,
# endpoints.endpoints.resource_manager = https://management.chinacloudapi.cn
base_url=endpoints.endpoints.resource_manager)
but seems it is not enough.
After struggling a lot, finally found the solution. Seems that no all the properties of Resource Manager Client are listed at:
https://learn.microsoft.com/en-us/python/api/azure-mgmt-resource/azure.mgmt.resource.resources.resourcemanagementclient?view=azure-python
There is a property named credential_scopes which should be set for change
credential_scopes=[CLOUD.endpoints.resource_manager + "/.default"])
so the function looks like
def __init__(self, subscriptionId, resourceGroup):
self.subscriptionId = subscriptionId
self.resourceGroup = resourceGroup
self.credentials = DefaultAzureCredential()
self.logger.info("Creating a client for deploying resources on subscription {}".format(self.subscriptionId))
self.client = ResourceManagementClient(self.credentials, self.subscriptionId,
base_url=CLOUD.endpoints.resource_manager,
credential_scopes=[CLOUD.endpoints.resource_manager + "/.default"])
I'm writing a program to backup my Google Photos library locally, and last week the code was working fine but in the last few days I've been repeatedly getting an error back from the API client saying that "photoslibrary v1" does not exist within Google's API library. The docs haven't changed at all - is there a problem on Google's end or is it me?
from googleapiclient import discovery
from httplib2 import Http
from oauth2client import file, client, tools
class Photos:
def __init__(self):
self.SCOPE = "https://www.googleapis.com/auth/photoslibrary"
self.CLIENT_SECRET = "client_id.json"
self.store = file.Storage("storage.json")
self.credentials = self.store.get()
if not self.credentials or self.credentials.invalid:
self.flow = client.flow_from_clientsecrets("client_id.json", self.SCOPE)
self.credentials = tools.run_flow(self.flow, self.store)
self.PHOTOS = discovery.build("photoslibrary", "v1", http=self.credentials.authorize(Http()))
photos = Photos()
googleapiclient.errors.UnknownApiNameOrVersion: name: photoslibrary version: v1
Try adding the parameter static_discovery=False
self.PHOTOS = discovery.build("photoslibrary", "v1", http=self.credentials.authorize(Http()),static_discovery=False)
I am trying to add a reply_url programmatically to an Azure app registration, but I receive an azure.graphrbac.models.graph_error_py3.GraphErrorException: Specified HTTP method is not allowed for the request target.
It fails when I try to update an existing application with new reply_urls.
SDK I am using is: azure-graphrbac==0.61.1
My code:
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac import GraphRbacManagementClient
from azure.graphrbac.models import ApplicationUpdateParameters
class GraphClient:
def __init__(self, client_id, client_secret, tenant_id, object_id):
self._credentials = ServicePrincipalCredentials(
client_id=client_id,
secret=client_secret,
tenant=tenant_id,
resource="https://graph.windows.net"
)
self._graph_client = GraphRbacManagementClient(
credentials=self._credentials,
tenant_id=tenant_id
)
self._object_id = object_id
self._application = self._graph_client.applications.get(self._object_id)
def get_reply_urls(self) -> List[str]:
return self._application.reply_urls
def add_reply_url(self, reply_url) -> None:
reply_urls: list = self.get_reply_urls()
self._graph_client.applications.patch(
self._object_id,
ApplicationUpdateParameters(
reply_urls=[
*reply_urls,
reply_url]
)
)
Could not reproduce your issue, use the same version of azure-graphrbac, I test your code on my side, it works fine.
testclient = GraphClient(client_id = "xxxxx",client_secret = "xxxxx", tenant_id = "xxxxx", object_id = "xxxxx")
testclient.add_reply_url(reply_url = "http://localhost:8085")
Check in the portal:
Also, I test the sdk directly, both work.
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac import GraphRbacManagementClient
from azure.graphrbac.models import ApplicationUpdateParameters
_credentials = ServicePrincipalCredentials(
client_id="xxxxx",
secret="xxxxx",
tenant="xxxxx",
resource="https://graph.windows.net"
)
_graph_client = GraphRbacManagementClient(
credentials=_credentials,
tenant_id="xxxxx"
)
app = _graph_client.applications.patch(
application_object_id = "xxxxx",
parameters = ApplicationUpdateParameters(reply_urls = ["http://localhost:8080","http://localhost:8081"])
)
Update call looks good however it depends on a legacy API (AAD Graph) and tools. It's strongly recommended to move to MS Graph which supports almost all Azure AD Graph operations and will fully support them all in a future. Applications being one of them.
You can use Requests-OAuthlib or Microsoft Graph Python Client Library for this.
I am using the following azurerm function in my code:
public_ips = azurerm.get_vmss_public_ips(access_token, SUBSCRIPTION_ID,
GROUP_NAME, CUScaleSet)
print(public_ips)
I am getting the following output:
{u'error': {u'message': u"No registered resource provider found for
location 'eastus' and API version '2019-03-01' for type
'virtualMachineScaleSets/publicIPAddresses'. The supported
api-versions are '2017-03-30, 2017-12-01, 2018-04-01, 2018-06-01,
2018-10-01'. The supported locations are 'eastus, eastus2, westus,
centralus, northcentralus, southcentralus, northeurope, westeurope,
eastasia, southeastasia, japaneast, japanwest, australiaeast,
australiasoutheast, australiacentral, brazilsouth, southindia,
centralindia, westindia, canadacentral, canadaeast, westus2,
westcentralus, uksouth, ukwest, koreacentral, koreasouth,
francecentral, southafricanorth, uaenorth'.", u'code':
u'NoRegisteredProviderFound'}}
NOTE: The same piece of code was running a few days ago.
If the requirement is to fetch all the IPs of the VMs in the VMSS instance, you can use the official Azure SDK for Python as follows:
# Imports
from azure.common.credentials import ServicePrincipalCredentials
from azure.mgmt.network import NetworkManagementClient
# Set subscription ID
SUBSCRIPTION_ID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
def get_credentials():
credentials = ServicePrincipalCredentials(
client_id='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
secret='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
tenant='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
)
return credentials
# Get credentials
credentials = get_credentials()
# Initialize management client
network_client = NetworkManagementClient(
credentials,
SUBSCRIPTION_ID
)
def get_vmss_vm_ips():
# List all network interfaces of the VMSS instance
vmss_nics = network_client.network_interfaces.list_virtual_machine_scale_set_network_interfaces(
"<VMSS Resource group name>", "<VMSS instance name>")
niclist = [nic.serialize() for nic in vmss_nics]
print "IP addresses in the given VM Scale Set:"
for nic in niclist:
ipconf = nic['properties']['ipConfigurations']
for ip in ipconf:
print ip['properties']['privateIPAddress']
# Get all IPs of VMs in VMSS
get_vmss_vm_ips()
References:
NetworkInterfacesOperations class
list_virtual_machine_scale_set_network_interfaces() method
Hope this helps!
I need to make online predictions from a model that is deployed in cloud ml engine. My code in python is similar to the one found in the docs (https://cloud.google.com/ml-engine/docs/tensorflow/online-predict):
service = googleapiclient.discovery.build('ml', 'v1')
name = 'projects/{}/models/{}'.format(project, model)
if version is not None:
name += '/versions/{}'.format(version)
response = service.projects().predict(
name=name,
body={'instances': instances}
).execute()
However, I receive the "instances" data from outside the script, I wonder if there is a way I could run this script without making the "service = googleapiclient.discovery.build('ml', 'v1')" each time before a request, since it takes time.
pd: this is my very first project on gcp. Thank you.
Something like this will work. You'll want to initialize your service globally then use that service instance to make your call.
import googleapiclient.discovery
AI_SERVICE = None
def ai_platform_init():
global AI_SERVICE
# Set GCP Authentication
credentials = os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')
# Path to your credentials
credentials_path = os.path.join(os.path.dirname(__file__), 'ai-platform-credentials.json')
if credentials is None and os.path.exists(credentials_path):
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = credentials_path
# Create AI Platform Service
if os.path.exists(credentials_path):
AI_SERVICE = googleapiclient.discovery.build('ml', 'v1', cache=MemoryCache())
# Initialize AI Platform on load.
ai_platform_init()
then later on, you can do something like this:
def call_ai_platform():
response = AI_SERVICE.projects().predict(name=name,
body={'instances': instances}).execute()
Bonus! in case you were curious about the MemoryCache class in the googleapiclient.discovery call, that was borrowed from another SO:
class MemoryCache():
"""A workaround for cache warnings from Google.
Check out: https://github.com/googleapis/google-api-python-client/issues/325#issuecomment-274349841
"""
_CACHE = {}
def get(self, url):
return MemoryCache._CACHE.get(url)
def set(self, url, content):
MemoryCache._CACHE[url] = content