Beispiel #1
0
def create_certificate(client,
                       vault_base_url,
                       certificate_name,
                       certificate_policy,
                       disabled=False,
                       expires=None,
                       not_before=None,
                       tags=None):
    cert_attrs = CertificateAttributes(not disabled, not_before, expires)
    logger.info(
        "Starting long running operation 'keyvault certificate create'")
    client.create_certificate(vault_base_url, certificate_name,
                              certificate_policy, cert_attrs, tags)

    if certificate_policy['issuer_parameters']['name'].lower() == 'unknown':
        # return immediately for a pending certificate
        return client.get_certificate_operation(vault_base_url,
                                                certificate_name)

    # otherwise loop until the certificate creation is complete
    while True:
        check = client.get_certificate_operation(vault_base_url,
                                                 certificate_name)
        if check.status != 'inProgress':
            logger.info("Long running operation 'keyvault certificate create' finished with result %s.", check)  # pylint: disable=line-too-long
            return check
        try:
            time.sleep(10)
        except KeyboardInterrupt:
            logger.info("Long running operation wait cancelled.")
            raise
        except Exception as client_exception:
            telemetry.set_exception(
                exception=client_exception,
                fault_type='cert-create-error',
                summary='Unexpected client exception during cert creation')
            message = getattr(client_exception, 'message', client_exception)

            try:
                message = str(message) + ' ' + json.loads(client_exception.response.text) \
                    ['error']['details'][0]['message']
            except:  #pylint: disable=bare-except
                pass

            raise CLIError('{}'.format(message))
    def __init__(self,
                 test_file,
                 test_name,
                 run_live=False,
                 debug=False,
                 debug_vcr=False,
                 skip_setup=False,
                 skip_teardown=False):
        super(VCRTestBase, self).__init__(test_name)
        self.test_name = test_name
        self.recording_dir = os.path.join(os.path.dirname(test_file),
                                          'recordings')
        self.cassette_path = os.path.join(self.recording_dir,
                                          '{}.yaml'.format(test_name))
        self.playback = os.path.isfile(self.cassette_path)

        if os.environ.get(LIVE_TEST_CONTROL_ENV, None) == 'True':
            self.run_live = True
        else:
            self.run_live = run_live

        self.skip_setup = skip_setup
        self.skip_teardown = skip_teardown
        self.success = False
        self.exception = None
        self.track_commands = os.environ.get(COMMAND_COVERAGE_CONTROL_ENV,
                                             None)
        self._debug = debug

        if not self.playback and ('--buffer' in sys.argv) and not run_live:
            self.exception = CLIError(
                'No recorded result provided for {}.'.format(self.test_name))

        if debug_vcr:
            import logging
            logging.basicConfig()
            vcr_log = logging.getLogger('vcr')
            vcr_log.setLevel(logging.INFO)
        self.my_vcr = vcr.VCR(
            cassette_library_dir=self.recording_dir,
            before_record_request=self._before_record_request,
            before_record_response=self._before_record_response,
            decode_compressed_response=True)
        self.my_vcr.register_matcher('custom', _custom_request_matcher)
        self.my_vcr.match_on = ['custom']
Beispiel #3
0
def _update_ssl_binding(resource_group_name,
                        name,
                        certificate_thumbprint,
                        ssl_type,
                        slot=None):
    client = web_client_factory()
    webapp = _generic_site_operation(resource_group_name, name, 'get')
    webapp_certs = client.certificates.list_by_resource_group(
        resource_group_name)
    for webapp_cert in webapp_certs:
        if webapp_cert.thumbprint == certificate_thumbprint:
            return _update_host_name_ssl_state(resource_group_name, name,
                                               webapp.location,
                                               webapp_cert.host_names[0],
                                               ssl_type,
                                               certificate_thumbprint, slot)
    raise CLIError("Certificate for thumbprint '{}' not found.".format(
        certificate_thumbprint))
Beispiel #4
0
def _validate_vm_create_availability_set(namespace):

    if namespace.availability_set:
        as_id = parse_resource_id(namespace.availability_set)
        name = as_id['name']
        rg = as_id.get('resource_group', namespace.resource_group_name)

        if not check_existence(name, rg, 'Microsoft.Compute',
                               'availabilitySets'):
            raise CLIError(
                "Availability set '{}' does not exist.".format(name))

        namespace.availability_set = resource_id(
            subscription=get_subscription_id(),
            resource_group=rg,
            namespace='Microsoft.Compute',
            type='availabilitySets',
            name=name)
Beispiel #5
0
def set_deployment_user(user_name, password=None):
    '''
    Update deployment credentials.(Note, all webapps in your subscription will be impacted)
    '''
    client = web_client_factory()
    user = User(location='not-really-needed')
    user.publishing_user_name = user_name
    if password is None:
        try:
            password = prompt_pass(msg='Password: '******'Please specify both username and password in non-interactive mode.'
            )

    user.publishing_password = password
    result = client.update_publishing_user(user)
    return result
Beispiel #6
0
def delete_policy(client, resource_group_name, vault_name, object_id=None, spn=None, upn=None): #pylint:disable=too-many-arguments
    """ Delete security policy settings for a Key Vault. """
    from azure.mgmt.keyvault.models import VaultCreateOrUpdateParameters
    object_id = _object_id_args_helper(object_id, spn, upn)
    vault = client.get(resource_group_name=resource_group_name,
                       vault_name=vault_name)
    prev_policies_len = len(vault.properties.access_policies)
    vault.properties.access_policies = [p for p in vault.properties.access_policies if \
                                        vault.properties.tenant_id.lower() != p.tenant_id.lower() \
                                        or object_id.lower() != p.object_id.lower()]
    if len(vault.properties.access_policies) == prev_policies_len:
        raise CLIError('No matching policies found')
    return client.create_or_update(resource_group_name=resource_group_name,
                                   vault_name=vault_name,
                                   parameters=VaultCreateOrUpdateParameters(
                                       location=vault.location,
                                       tags=vault.tags,
                                       properties=vault.properties))
Beispiel #7
0
def _arm_get_resource_by_name(resource_name, resource_type):
    '''Returns the ARM resource in the current subscription with resource_name.
    :param str resource_name: The name of resource
    :param str resource_type: The type of resource
    '''
    result = get_resources_in_subscription(resource_type)
    elements = [
        item for item in result if item.name.lower() == resource_name.lower()
    ]

    if len(elements) == 0:
        return None
    elif len(elements) == 1:
        return elements[0]
    else:
        raise CLIError(
            'More than one resources with type {} are found with name: {}'.
            format(resource_type, resource_name))
Beispiel #8
0
def show_options(instance, part, path):
    options = instance.__dict__ if hasattr(instance, '__dict__') else instance
    parent = '.'.join(path[:-1]).replace('.[', '[')
    error_message = "Couldn't find '{}' in '{}'.".format(part, parent)
    if isinstance(options, dict):
        options = options.keys()
        options = sorted([make_camel_case(x) for x in options])
        error_message = '{} Available options: {}'.format(
            error_message, options)
    elif isinstance(options, list):
        options = "index into the collection '{}' with [<index>] or [<key=value>]".format(
            parent)
        error_message = '{} Available options: {}'.format(
            error_message, options)
    else:
        error_message = "{} '{}' does not support further indexing.".format(
            error_message, parent)
    raise CLIError(error_message)
Beispiel #9
0
def handle_feedback():
    try:
        print(MESSAGES['intro'])
        score = _prompt_net_promoter_score()
        response_do_well = None
        response_what_changes = None
        if score == 10:
            response_do_well = prompt(MESSAGES['prompt_do_well'])
        else:
            response_what_changes = prompt(MESSAGES['prompt_what_changes'])
        email_address = prompt(MESSAGES['prompt_email_addr'])
        _send_feedback(score, response_what_changes, response_do_well,
                       email_address)
        print(MESSAGES['thanks'])
    except NoTTYException:
        raise CLIError('This command is interactive and no tty available.')
    except (EOFError, KeyboardInterrupt):
        print()
Beispiel #10
0
def iot_hub_create(client,
                   hub_name,
                   resource_group_name,
                   location=None,
                   sku=IotHubSku.f1.value,
                   unit=1):
    name_availability = client.check_name_availability(hub_name)
    logger.info('name availability info: %s', name_availability)
    if name_availability is not None and not name_availability.name_available:
        raise CLIError(name_availability.message)
    location = _ensure_location(resource_group_name, location)
    iot_hub_description = IotHubDescription(location=location,
                                            sku=IotHubSkuInfo(name=sku,
                                                              capacity=unit))
    result = client.create_or_update(resource_group_name=resource_group_name,
                                     resource_name=hub_name,
                                     iot_hub_description=iot_hub_description)
    return result
    def set_up(self):
        super(DataLakeStoreFileAccessScenarioTest, self).set_up()

        # create ADLS account
        self.cmd(
            'dls account create -g {} -n {} -l {} --disable-encryption'.format(
                self.resource_group, self.adls_name, self.location))
        result = self.cmd('dls account show -g {} -n {}'.format(
            self.resource_group, self.adls_name))
        while result['provisioningState'] != 'Succeeded' and result[
                'provisioningState'] != 'Failed':
            time.sleep(5)
            result = self.cmd('dls account show -g {} -n {}'.format(
                self.resource_group, self.adls_name))

        if result['provisioningState'] == 'Failed':
            raise CLIError(
                'Failed to create the adls account, tests cannot proceed!')
def get_data_service_client(service_type,
                            account_name,
                            account_key,
                            connection_string=None,
                            sas_token=None):
    logger.info('Getting data service client service_type=%s',
                service_type.__name__)
    try:
        client = service_type(account_name=account_name,
                              account_key=account_key,
                              connection_string=connection_string,
                              sas_token=sas_token)
    except ValueError:
        raise CLIError(
            'Unable to obtain data client. Check your connection parameters.')
    # TODO: enable Fiddler
    client.request_callback = _add_headers
    return client
Beispiel #13
0
def _normalize_extension_version(publisher, vm_extension_name, version,
                                 location):
    if not version:
        result = load_extension_images_thru_services(publisher,
                                                     vm_extension_name,
                                                     None,
                                                     location,
                                                     show_latest=True)
        if not result:
            raise CLIError(
                'Failed to find the latest version for the extension "{}"'.
                format(vm_extension_name))

        #with 'show_latest' enabled, we will only get one result.
        version = result[0]['version']

    version = _trim_away_build_number(version)
    return version
Beispiel #14
0
def add_certificate_contact(client,
                            vault_base_url,
                            contact_email,
                            contact_name=None,
                            contact_phone=None):
    """ Add a contact to the specified vault to receive notifications of certificate operations. """
    from azure.cli.command_modules.keyvault.keyvaultclient.generated.models import \
        (Contact, Contacts, KeyVaultErrorException)
    try:
        contacts = client.get_certificate_contacts(vault_base_url)
    except KeyVaultErrorException:
        contacts = Contacts([])
    contact = Contact(contact_email, contact_name, contact_phone)
    if any((x for x in contacts.contact_list
            if x.email_address == contact_email)):
        raise CLIError("contact '{}' already exists".format(contact_email))
    contacts.contact_list.append(contact)
    return client.set_certificate_contacts(vault_base_url, contacts)
Beispiel #15
0
def create_service_principal_for_rbac(name=None, password=None, years=1,
                                      scopes=None, role=None):
    '''create a service principal that can access or modify resources
    :param str name: an unique uri. If missing, the command will generate one.
    :param str password: the password used to login. If missing, command will generate one.
    :param str years: Years the password will be valid.
    :param str scopes: space separated scopes the service principal's role assignment applies to.
    :param str role: role the service principal has on the resources. only use with 'resource-ids'.
    '''
    if bool(scopes) != bool(role):
        raise CLIError("'--scopes' and '--role' must be used together.")
    client = _graph_client_factory()
    start_date = datetime.datetime.now()
    app_display_name = 'azure-cli-' + start_date.strftime('%Y-%m-%d-%H-%M-%S')
    if name is None:
        name = 'http://' + app_display_name # just a valid uri, no need to exist

    end_date = start_date + relativedelta(years=years)
    password = password or str(uuid.uuid4())
    aad_application = create_application(client.applications, display_name=app_display_name, #pylint: disable=too-many-function-args
                                         homepage='http://'+app_display_name,
                                         identifier_uris=[name],
                                         available_to_other_tenants=False,
                                         password=password,
                                         start_date=start_date,
                                         end_date=end_date)
    #pylint: disable=no-member
    aad_sp = _create_service_principal(aad_application.app_id, bool(scopes))
    oid = aad_sp.output.object_id if scopes else aad_sp.object_id

    if scopes:
        #It is possible the SP has not been propagated to all servers, so creating assignments
        #might fail. The reliable workaround is to call out the server where creation occurred.
        #pylint: disable=protected-access
        session_key = aad_sp.response.headers._store['ocp-aad-session-key'][1]
        for scope in scopes:
            _create_role_assignment(role, oid, None, scope, ocp_aad_session_key=session_key)

    return {
        'appId': aad_application.app_id,
        'password': password,
        'name': name,
        'tenant': client.config.tenant_id
        }
Beispiel #16
0
    def __call__(self, poller):
        from msrest.exceptions import ClientException
        logger.info("Starting long running operation '%s'", self.start_msg)
        correlation_message = ''
        while not poller.done():
            try:
                # pylint: disable=protected-access
                correlation_id = json.loads(
                    poller._response.__dict__['_content'])['properties']['correlationId']

                correlation_message = 'Correlation ID: {}'.format(correlation_id)
            except:  # pylint: disable=bare-except
                pass

            try:
                self._delay()
            except KeyboardInterrupt:
                logger.error('Long running operation wait cancelled.  %s', correlation_message)
                raise
        try:
            result = poller.result()
        except ClientException as client_exception:
            telemetry.set_exception(
                client_exception,
                fault_type='failed-long-running-operation',
                summary='Unexpected client exception in {}.'.format(LongRunningOperation.__name__))
            message = getattr(client_exception, 'message', client_exception)

            try:
                message = '{} {}'.format(
                    str(message),
                    json.loads(client_exception.response.text)['error']['details'][0]['message'])
            except:  # pylint: disable=bare-except
                pass

            cli_error = CLIError('{}  {}'.format(message, correlation_message))
            # capture response for downstream commands (webapp) to dig out more details
            setattr(cli_error, 'response', getattr(client_exception, 'response', None))
            raise cli_error

        logger.info("Long running operation '%s' completed with result %s",
                    self.start_msg, result)
        return result
Beispiel #17
0
def create_adla_catalog_credential(client,
                                   account_name,
                                   database_name,
                                   credential_name,
                                   credential_user_name,
                                   uri,
                                   credential_user_password=None):

    if not credential_user_password:
        try:
            credential_user_password = prompt_pass('Password:'******'Please specify both --user-name and --password in non-interactive mode.')

    create_params = DataLakeAnalyticsCatalogCredentialCreateParameters(credential_user_password,
                                                                       uri,
                                                                       credential_user_name)
    client.create_credential(account_name, database_name, credential_name, create_params)
Beispiel #18
0
 def _get_detail_error(self, ex):
     try:
         detail = json.loads(ex.response.text)['Message']
         if self._creating_plan:
             if 'Requested features are not supported in region' in detail:
                 detail = (
                     "Plan with linux worker is not supported in current region. "
                     +
                     "Run 'az appservice list-locations --linux-workers-enabled' "
                     + "to cross check")
             elif 'Not enough available reserved instance servers to satisfy' in detail:
                 detail = (
                     "Plan with Linux worker can only be created in a group "
                     +
                     "which has never contained a Windows worker. Please use "
                     + "a new resource group. Original error:" + detail)
         return CLIError(detail)
     except:  #pylint: disable=bare-except
         return ex
Beispiel #19
0
 def _get_detail_error(self, ex):
     try:
         detail = json.loads(ex.response.text)['Message']
         if self._creating_plan:
             if 'Requested features are not supported in region' in detail:
                 detail = (
                     "Plan with linux worker is not supported in current region. For "
                     +
                     "supported regions, please refer to https://docs.microsoft.com/en-us/"
                     "azure/app-service-web/app-service-linux-intro")
             elif 'Not enough available reserved instance servers to satisfy' in detail:
                 detail = (
                     "Plan with Linux worker can only be created in a group "
                     +
                     "which has never contained a Windows worker. Please use "
                     + "a new resource group. Original error:" + detail)
         return CLIError(detail)
     except:  #pylint: disable=bare-except
         return ex
Beispiel #20
0
def get_data_service_client(service_type, account_name, account_key, connection_string=None,  # pylint: disable=too-many-arguments
                            sas_token=None, endpoint_suffix=None):
    logger.debug('Getting data service client service_type=%s', service_type.__name__)
    try:
        client_kwargs = {'account_name': account_name,
                         'account_key': account_key,
                         'connection_string': connection_string,
                         'sas_token': sas_token}
        if endpoint_suffix:
            client_kwargs['endpoint_suffix'] = endpoint_suffix
        client = service_type(**client_kwargs)
    except ValueError as exc:
        if _ERROR_STORAGE_MISSING_INFO in str(exc):
            raise ValueError(exc)
        else:
            raise CLIError('Unable to obtain data client. Check your connection parameters.')
    # TODO: enable Fiddler
    client.request_callback = _add_headers
    return client
Beispiel #21
0
def acr_credential_show(registry_name, resource_group_name=None):
    '''Get admin username and password for a container registry.
    :param str registry_name: The name of container registry
    :param str resource_group_name: The name of resource group
    '''
    registry = get_registry_by_name(registry_name)
    if registry is None:
        registry_not_found(registry_name)

    if resource_group_name is None:
        resource_group_name = get_resource_group_name_by_resource_id(registry.id)

    client = get_acr_service_client().registries
    if registry.properties.admin_user_enabled:
        return client.get_credentials(resource_group_name, registry_name)
    else:
        raise CLIError(
            'Admin user is not enabled for the container registry with name: {}'\
            .format(registry_name))
Beispiel #22
0
def _install_or_update(package_list, link, private, pre, show_logs=False):
    options = ['--isolated', '--disable-pip-version-check', '--upgrade']
    if pre:
        options.append('--pre')
    if not show_logs:
        options.append('--quiet')
    pkg_index_options = ['--find-links', link] if link else []
    if private:
        package_index_url = az_config.get('component',
                                          'package_index_url',
                                          fallback=None)
        package_index_trusted_host = az_config.get('component', 'package_index_trusted_host', fallback=None)  #pylint: disable=line-too-long
        if package_index_url:
            pkg_index_options += ['--extra-index-url', package_index_url]
        else:
            raise CLIError('AZURE_COMPONENT_PACKAGE_INDEX_URL environment variable not set and not specified in config. '  #pylint: disable=line-too-long
                           'AZURE_COMPONENT_PACKAGE_INDEX_TRUSTED_HOST may also need to be set.')  #pylint: disable=line-too-long
        pkg_index_options += ['--trusted-host', package_index_trusted_host] if package_index_trusted_host else []  #pylint: disable=line-too-long
    pip.main(['install'] + options + package_list + pkg_index_options)
Beispiel #23
0
def iot_hub_create(client, hub_name, resource_group_name,
                   location=None, sku=IotHubSku.f1.value, unit=1):
    name_availability = client.check_name_availability(hub_name)
    logger.info('name availability info: %s', name_availability)
    if name_availability is not None and not name_availability.name_available:
        raise CLIError(name_availability.message)

    if location is None:
        logger.info('Location is none. Use location of resource group as default.')
        resource_group_client = resource_service_factory().resource_groups
        group_info = resource_group_client.get(resource_group_name)
        location = group_info.location
        logger.info('Location to use: %s', location)

    iot_hub_description = IotHubDescription(location=location,
                                            sku=IotHubSkuInfo(name=sku, capacity=unit))
    result = client.create_or_update(resource_group_name=resource_group_name, resource_name=hub_name,
                                     iot_hub_description=iot_hub_description)
    return result
Beispiel #24
0
    def set_active_subscription(self, subscription):  #take id or name
        subscriptions = self.load_cached_subscriptions()

        subscription = subscription.lower()
        result = [
            x for x in subscriptions if subscription in
            [x[_SUBSCRIPTION_ID].lower(), x[_SUBSCRIPTION_NAME].lower()]
        ]

        if len(result) != 1:
            raise CLIError(
                'The subscription of "{}" does not exist or has more than'
                ' one match.'.format(subscription))

        for s in subscriptions:
            s[_IS_DEFAULT_SUBSCRIPTION] = False
        result[0][_IS_DEFAULT_SUBSCRIPTION] = True

        self._cache_subscriptions_to_local_storage(subscriptions)
Beispiel #25
0
def _deploy_arm_template_core(resource_group_name,
                              template_file=None,
                              template_uri=None,
                              deployment_name=None,
                              parameters=None,
                              mode='incremental',
                              validate_only=False,
                              no_wait=False):
    from azure.mgmt.resource.resources.models import DeploymentProperties, TemplateLink

    if bool(template_uri) == bool(template_file):
        raise CLIError(
            'please provide either template file path or uri, but not both')

    if parameters:
        parameters = json.loads(parameters)
        if parameters:
            parameters = parameters.get('parameters', parameters)

    template = None
    template_link = None
    if template_uri:
        template_link = TemplateLink(uri=template_uri)
    else:
        template = get_file_json(template_file)

    properties = DeploymentProperties(template=template,
                                      template_link=template_link,
                                      parameters=parameters,
                                      mode=mode)

    smc = get_mgmt_service_client(ResourceManagementClient)
    if validate_only:
        return smc.deployments.validate(resource_group_name,
                                        deployment_name,
                                        properties,
                                        raw=no_wait)
    else:
        return smc.deployments.create_or_update(resource_group_name,
                                                deployment_name,
                                                properties,
                                                raw=no_wait)
Beispiel #26
0
def _get_remote_url():
    """
    Tries to find a remote for the repo in the current folder.
    If only one remote is present return that remote,
    if more than one remote is present it looks for origin.
    """
    try:
        remotes = check_output(['git', 'remote']).strip().splitlines()
        remote_url = ''
        if len(remotes) == 1:
            remote_url = check_output(['git', 'remote', 'get-url', remotes[0].decode()]).strip()
        else:
            remote_url = check_output(['git', 'remote', 'get-url', 'origin']).strip()
    except ValueError as e:
        logger.debug(e)
        raise CLIError(
            "A default remote was not found for the current folder. \
            Please run this command in a git repository folder with \
            an 'origin' remote or specify a remote using '--remote-url'")
    return remote_url.decode()
Beispiel #27
0
def add_certificate_issuer_admin(client, vault_base_url, issuer_name, email, first_name=None,
                                 last_name=None, phone=None):
    """ Add admin details for a specified certificate issuer. """
    from azure.keyvault.generated.models import \
        (AdministratorDetails, KeyVaultErrorException)

    issuer = client.get_certificate_issuer(vault_base_url, issuer_name)
    org_details = issuer.organization_details
    admins = org_details.admin_details
    if any((x for x in admins if x.email_address == email)):
        raise CLIError("admin '{}' already exists".format(email))
    new_admin = AdministratorDetails(first_name, last_name, email, phone)
    admins.append(new_admin)
    org_details.admin_details = admins
    result = client.set_certificate_issuer(
        vault_base_url, issuer_name, issuer.provider, issuer.credentials, org_details,
        issuer.attributes)
    created_admin = next(x for x in result.organization_details.admin_details \
        if x.email_address == email)
    return created_admin
Beispiel #28
0
def acs_browse(resource_group, name, disable_browser=False):
    """
    Opens a browser to the web interface for the cluster orchestrator

    :param name: Name of the target Azure container service instance.
    :type name: String
    :param resource_group_name:  Name of Azure container service's resource group.
    :type resource_group_name: String
    :param disable_browser: If true, don't launch a web browser after estabilishing the proxy
    :type disable_browser: bool
    """
    acs_info = _get_acs_info(name, resource_group)
    orchestrator_type = acs_info.orchestrator_profile.orchestrator_type # pylint: disable=no-member

    if  orchestrator_type == 'kubernetes':
        return k8s_browse(disable_browser)
    elif orchestrator_type == 'dcos':
        return _dcos_browse_internal(acs_info, disable_browser)
    else:
        raise CLIError('Unsupported orchestrator type {} for browse'.format(orchestrator_type))
Beispiel #29
0
def _generate_lb_id_list_from_names_or_ids(namespace, prop, child_type):
    raw = getattr(namespace, prop)
    if not raw:
        return
    raw = raw if isinstance(raw, list) else [raw]
    result = []
    for item in raw:
        if is_valid_resource_id(item):
            result.append({'id': item})
        else:
            if not namespace.load_balancer_name:
                raise CLIError(
                    'Unable to process {}. Please supply a well-formed ID or '
                    '--lb-name.'.format(item))
            else:
                result.append({
                    'id':
                    _generate_lb_subproperty_id(namespace, child_type, item)
                })
    setattr(namespace, prop, result)
Beispiel #30
0
def _create_file_and_directory_from_blob(file_service, blob_service, share, container, sas,
                                         blob_name,
                                         destination_dir=None, metadata=None, timeout=None,
                                         existing_dirs=None):
    """
    Copy a blob to file share and create the directory if needed.
    """
    blob_url = blob_service.make_blob_url(container, blob_name, sas_token=sas)
    full_path = os.path.join(destination_dir, blob_name) if destination_dir else blob_name
    file_name = os.path.basename(full_path)
    dir_name = os.path.dirname(full_path)
    _make_directory_in_files_share(file_service, share, dir_name, existing_dirs)

    try:
        file_service.copy_file(share, dir_name, file_name, blob_url, metadata, timeout)
        return file_service.make_file_url(share, dir_name, file_name)
    except AzureException:
        error_template = 'Failed to copy blob {} to file share {}. Please check if you have ' + \
                         'permission to read source or set a correct sas token.'
        raise CLIError(error_template.format(blob_name, share))