Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/azure-cli/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ Release History

* Update azure-mgmt-resource package to use 6.0.0

**BotService**

* [Breaking change] Remove '--version' flag from preview command 'az bot create'. Only v4 SDK bots are supported.
* Add name availability check for 'az bot create'.
* Add support for updating the icon URL for a bot via 'az bot update'.
* Add support for updating a Direct Line channel via 'az bot directline update'.
* Add '--enable-enhanced-auth' flag support to 'az bot directline create'.
* The following command groups are GA and not in preview: 'az bot authsetting'.
* The following commands in 'az bot' are GA and not in preview: 'create', 'prepare-deploy', 'show', 'delete', 'update'.
* Fix 'az bot prepare-deploy' changing '--proj-file-path' value to lower case (e.g. "Test.csproj" to "test.csproj").

**Compute**

* vmss create/update: Add --scale-in-policy, which decides which virtual machines are chosen for removal when a VMSS is scaled-in.
Expand Down
8 changes: 8 additions & 0 deletions src/azure-cli/azure/cli/command_modules/botservice/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@
text: |-
az bot directline create -n botName -g MyResourceGroup --disablev1
"""
helps['bot directline update'] = """
type: command
short-summary: Update the DirectLine Channel on a bot with only v3 protocol enabled.
examples:
- name: Update the DirectLine Channel for a bot.
text: |-
az bot directline update -n botName -g MyResourceGroup --disablev1
"""
helps['bot telegram create'] = """
type: command
short-summary: Create the Telegram Channel on a bot.
Expand Down
16 changes: 13 additions & 3 deletions src/azure-cli/azure/cli/command_modules/botservice/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,9 @@ def load_arguments(self, _):
c.argument('description', options_list=['--description', '-d'], help='The description of the bot.', arg_group='Registration Bot Specific')
c.argument('endpoint', options_list=['-e', '--endpoint'], help='The messaging endpoint of the bot.', arg_group='Registration Bot Specific')
c.argument('msa_app_id', options_list=['--appid'], help='The Microsoft account ID (MSA ID) to be used with the bot.')
c.argument('password', options_list=['-p', '--password'], help='The Microsoft account (MSA) password for the bot. Used to authorize messages being sent to the bot.')
c.argument('password', options_list=['-p', '--password'], help='The Microsoft account (MSA) password for the bot. Used to authorize messages being sent from the bot. Required when "--kind" is "webapp".', arg_group='Web App Bot Specific')
c.argument('tags', arg_type=tags_type)
c.argument('language', options_list=['--lang'], arg_type=get_enum_type(SUPPORTED_LANGUAGES), help='The language to be used to create the bot.', arg_group='Web Bot Specific')
c.argument('version', options_list=['-v', '--version'], help='The Microsoft Bot Builder SDK version to be used to create the bot', arg_type=get_enum_type(['v4']), arg_group='Web Bot Specific')
c.argument('language', options_list=['--lang'], arg_type=get_enum_type(SUPPORTED_LANGUAGES), help='The language to be used to create the bot.', arg_group='Web App Bot Specific')
c.argument('deploy_echo', options_list=['--echo'], arg_type=get_three_state_flag(), help='Deploy an Echo Bot template to the newly created v4 Web App Bot.', arg_group='V4 Bot Templates')

with self.argument_context('bot publish') as c:
Expand Down Expand Up @@ -106,6 +105,7 @@ def load_arguments(self, _):
arg_group='Bot Analytics/Application Insights',
help='Azure Application Insights Application ID used to read bot analytics data. Provide an Id if '
'you want to view analytics about your bot in the Analytics blade.')
c.argument('icon_url', help='Icon URL for bot avatar. Accepts PNG files with file size limit of 30KB.')

with self.argument_context('bot prepare-publish') as c:
c.argument('proj_file_path', options_list=['--proj-file-path', c.deprecate(target='--proj-name',
Expand Down Expand Up @@ -163,6 +163,16 @@ def load_arguments(self, _):
c.argument('site_name', options_list=['-s', '--site-name'], help='Name of the Directline channel site.')
c.argument('is_v1_disabled', options_list=['--disablev1'], help='If true, v1 protocol will be disabled on the channel', arg_type=get_three_state_flag())
c.argument('is_v3_disabled', options_list=['--disablev3'], help='If true, v3 protocol will be disabled on the channel.', arg_type=get_three_state_flag())
c.argument('enable_enhanced_auth', help='If true, enables enhanced authentication features. Must be true for --trusted-origins parameters to work.', arg_type=get_three_state_flag())
c.argument('trusted_origins', nargs='+', help='Space separated Trusted Origins URLs (must use HTTPS) e.g. --trusted-origins https://mybotsite1.azurewebsites.net https://mybotsite2.azurewebsites.net')

with self.argument_context('bot directline update') as c:
c.argument('is_disabled', options_list=['--add-disabled'], arg_type=get_three_state_flag(), help='Add the channel in a disabled state.')
c.argument('site_name', options_list=['-s', '--site-name'], help='Name of the Directline channel site.')
c.argument('is_v1_disabled', options_list=['--disablev1'], help='If true, v1 protocol will be disabled on the channel', arg_type=get_three_state_flag())
c.argument('is_v3_disabled', options_list=['--disablev3'], help='If true, v3 protocol will be disabled on the channel.', arg_type=get_three_state_flag())
c.argument('enable_enhanced_auth', help='If true, enables enhanced authentication features. Must be true for --trusted-origins parameters to work.', arg_type=get_three_state_flag())
c.argument('trusted_origins', nargs='+', help='Space separated Trusted Origins URLs (must use HTTPS) e.g. --trusted-origins https://mybotsite1.azurewebsites.net https://mybotsite2.azurewebsites.net')

with self.argument_context('bot telegram create') as c:
c.argument('is_disabled', options_list=['--add-disabled'], arg_type=get_three_state_flag(), help='Add the channel in a disabled state.')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,13 @@ def deploy_arm_template(cli_ctx, resource_group_name, # pylint: disable=too-man

@staticmethod
def create_app(cmd, logger, client, resource_group_name, resource_name, description, kind, appid, password, # pylint:disable=too-many-statements
location, sku_name, language, version, bot_template_type):
location, sku_name, language, bot_template_type):
kind = 'sdk' if kind == 'webapp' else kind
(zip_url, template_name) = BotTemplateDeployer.__retrieve_bot_template_link(version,
language,
(zip_url, template_name) = BotTemplateDeployer.__retrieve_bot_template_link(language,
bot_template_type)

logger.debug('Detected SDK version %s, kind %s and programming language %s. Using the following template: %s.',
version, kind, language, zip_url)
logger.debug('Detected kind %s and programming language %s. Using the following template: %s.',
kind, language, zip_url)

site_name = re.sub(r'[^a-z0-9\-]', '', resource_name[:40].lower())

Expand Down Expand Up @@ -140,8 +139,8 @@ def _try_parse_json_object(value):
return parameters

@staticmethod
def __retrieve_bot_template_link(version, language, bot_template_type):
if version == 'v4' and not bot_template_type:
def __retrieve_bot_template_link(language, bot_template_type):
if not bot_template_type:
return '', BotTemplateDeployer.v4_webapp_template_name

response = requests.get('https://dev.botframework.com/api/misc/bottemplateroot')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.mgmt.botservice.models import BotChannel
from azure.mgmt.botservice.models import (
BotChannel,
DirectLineChannel,
DirectLineChannelProperties,
DirectLineSite)


def create_channel(client, channel, channel_name, resource_group_name, resource_name):
Expand All @@ -19,6 +23,25 @@ def create_channel(client, channel, channel_name, resource_group_name, resource_
)


def update_channel(client, channel, channel_name, resource_group_name, resource_name):
bot_channel_data = client.get(
resource_group_name,
resource_name,
channel_name)
sites = bot_channel_data.properties.properties.sites

for site in sites:
if site.site_name == channel.properties.sites[0].site_name:
channel.properties.sites[0].site_id = site.site_id

return client.update(
resource_group_name=resource_group_name,
resource_name=resource_name,
channel_name=channel_name,
properties=channel
)


def facebook_create(client, resource_group_name, resource_name, page_id, app_id, app_secret, access_token, is_disabled=None): # pylint: disable=line-too-long
from azure.mgmt.botservice.models import FacebookChannel, FacebookChannelProperties, FacebookPage
channel = FacebookChannel(
Expand Down Expand Up @@ -91,19 +114,45 @@ def kik_create(client, resource_group_name, resource_name, user_name, api_key, i


def directline_create(client, resource_group_name, resource_name, is_disabled=None,
is_v1_disabled=None, is_v3_disabled=None, site_name='Default Site'):
from azure.mgmt.botservice.models import DirectLineChannel, DirectLineChannelProperties, DirectLineSite
is_v1_disabled=None, is_v3_disabled=None, site_name='Default Site',
enable_enhanced_auth=False, trusted_origins=None):
if not trusted_origins:
trusted_origins = []
channel = DirectLineChannel(
properties=DirectLineChannelProperties(
sites=[DirectLineSite(
site_name=site_name,
is_enabled=not is_disabled,
is_v1_enabled=not is_v1_disabled,
is_v3_enabled=not is_v3_disabled,
is_secure_site_enabled=enable_enhanced_auth,
trusted_origins=trusted_origins
)]
)
)
return create_channel(
client, channel, 'DirectLineChannel',
resource_group_name, resource_name)


def directline_update(client, resource_group_name, resource_name, is_disabled=None,
is_v1_disabled=None, is_v3_disabled=None, site_name='Default Site',
enable_enhanced_auth=False, trusted_origins=None):
if not trusted_origins:
trusted_origins = []
channel = DirectLineChannel(
properties=DirectLineChannelProperties(
sites=[DirectLineSite(
site_name=site_name,
is_enabled=not is_disabled,
is_v1_enabled=not is_v1_disabled,
is_v3_enabled=not is_v3_disabled
is_v3_enabled=not is_v3_disabled,
is_secure_site_enabled=enable_enhanced_auth,
trusted_origins=trusted_origins
)]
)
)
return create_channel(client, channel, 'DirectLineChannel', resource_group_name, resource_name)
return update_channel(client, channel, 'DirectLineChannel', resource_group_name, resource_name)


def telegram_create(client, resource_group_name, resource_name, access_token, is_disabled=None, is_validated=None):
Expand Down
14 changes: 8 additions & 6 deletions src/azure-cli/azure/cli/command_modules/botservice/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ def load_command_table(self, _):

with self.command_group('bot', botOperations_commandType) as g:
g.custom_command('create', 'create')
g.custom_command('publish', 'publish_app')
g.custom_command('download', 'download_app')
g.custom_command('prepare-publish', 'prepare_publish')
g.custom_command('publish', 'publish_app', is_preview=True)
g.custom_command('download', 'download_app', is_preview=True)
g.custom_command('prepare-publish', 'prepare_publish', is_preview=True)
g.custom_command('prepare-deploy', 'prepare_webapp_deploy')

with self.command_group('bot', botServices_commandType) as g:
Expand All @@ -68,15 +68,17 @@ def load_command_table(self, _):
g.custom_command('update', 'update')

for channel in ['facebook', 'email', 'msteams', 'skype', 'kik', 'directline', 'telegram', 'sms', 'slack']:
with self.command_group('bot {}'.format(channel), channelOperationsCreate_commandType) as g:
with self.command_group('bot {}'.format(channel), channelOperationsCreate_commandType, is_preview=True) as g:
g.command('create', '{}_create'.format(channel))
if channel == 'directline':
g.command('update', '{}_update'.format(channel))

with self.command_group('bot {}'.format(channel), channelOperations_commandType) as g:
with self.command_group('bot {}'.format(channel), channelOperations_commandType, is_preview=True) as g:
g.command('show', '{}_get'.format(channel))
g.command('delete', '{}_delete'.format(channel))

with self.command_group('bot webchat', channelOperations_commandType) as g:
g.command('show', 'webchat_get')

with self.command_group('bot', is_preview=True):
with self.command_group('bot'):
pass
35 changes: 22 additions & 13 deletions src/azure-cli/azure/cli/command_modules/botservice/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from azure.cli.command_modules.botservice.bot_template_deployer import BotTemplateDeployer
from azure.cli.command_modules.botservice.constants import CSHARP, JAVASCRIPT, TYPESCRIPT
from azure.cli.command_modules.botservice.kudu_client import KuduClient
from azure.cli.command_modules.botservice.name_availability import NameAvailability
from azure.cli.command_modules.botservice.web_app_operations import WebAppOperations
from azure.mgmt.botservice.models import (
Bot,
Expand Down Expand Up @@ -74,15 +75,26 @@ def __prepare_configuration_file(cmd, resource_group_name, kudu_client, folder_p
f.write(json.dumps(existing))


def create(cmd, client, resource_group_name, resource_name, kind, msa_app_id, password, language=None, # pylint: disable=too-many-locals, too-many-statements
def create(cmd, client, resource_group_name, resource_name, kind, msa_app_id, password=None, language=None, # pylint: disable=too-many-locals, too-many-statements, inconsistent-return-statements
description=None, display_name=None, endpoint=None, tags=None, location='Central US',
sku_name='F0', version='v4', deploy_echo=None):
sku_name='F0', deploy_echo=None):
# Kind parameter validation
kind = kind.lower()
registration_kind = 'registration'
bot_kind = 'bot'
webapp_kind = 'webapp'

# Mapping: registration is deprecated, we now use 'bot' kind for registration bots
if kind == registration_kind:
kind = bot_kind

# Check the resource name availability for the bot.
name_response = NameAvailability.check_name_availability(client, resource_name, kind)
if not name_response.valid:
# If the name is unavailable, gracefully exit and log the reason for the user.
raise CLIError('Unable to create a bot with a name of "{0}".\nReason: {1}'
.format(resource_name, name_response.message))

if resource_name.find(".") > -1:
logger.warning('"." found in --name parameter ("%s"). "." is an invalid character for Azure Bot resource names '
'and will been removed.', resource_name)
Expand All @@ -98,17 +110,10 @@ def create(cmd, client, resource_group_name, resource_name, kind, msa_app_id, pa
raise CLIError("--appid must be a valid GUID from a Microsoft Azure AD Application Registration. See "
"https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app for "
"more information on App Registrations. See 'az bot create --help' for more CLI information.")
if not password:
raise CLIError("--password cannot have a length of 0. This value is used to authorize calls to your bot. See "
"'az bot create --help'.`")

# If display name was not provided, just use the resource name
display_name = display_name or resource_name

# Mapping: registration is deprecated, we now use 'bot' kind for registration bots
if kind == registration_kind:
kind = bot_kind

logger.info('Creating Azure Bot Service.')

# Registration bots: simply call ARM and create the bot
Expand Down Expand Up @@ -141,6 +146,10 @@ def create(cmd, client, resource_group_name, resource_name, kind, msa_app_id, pa
parameters=parameters
)

if not password:
raise CLIError("--password cannot have a length of 0 for Web App Bots. This value is used to authorize calls "
"to your bot. See 'az bot create --help'.")

# Web app bots require deploying custom ARM templates, we do that in a separate method
logger.info('Detected kind %s, validating parameters for the specified kind.', kind)

Expand All @@ -153,7 +162,7 @@ def create(cmd, client, resource_group_name, resource_name, kind, msa_app_id, pa

creation_results = BotTemplateDeployer.create_app(
cmd, logger, client, resource_group_name, resource_name, description, kind, msa_app_id, password,
location, sku_name, language, version, bot_template_type)
location, sku_name, language, bot_template_type)

return creation_results

Expand Down Expand Up @@ -529,8 +538,7 @@ def does_file_exist(file_name):

with open(os.path.join(code_dir, '.deployment'), 'w') as f:
f.write('[config]\n')
proj_file = proj_file_path.lower()
proj_file = proj_file if proj_file.endswith('.csproj') else proj_file + '.csproj'
proj_file = proj_file_path if proj_file_path.lower().endswith('.csproj') else proj_file_path + '.csproj'
f.write('SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore "{0}"\n'.format(proj_file))
logger.info('.deployment file successfully created.')
return True
Expand Down Expand Up @@ -647,7 +655,7 @@ def publish_app(cmd, client, resource_group_name, resource_name, code_dir=None,

def update(client, resource_group_name, resource_name, endpoint=None, description=None,
display_name=None, tags=None, sku_name=None, app_insights_key=None,
app_insights_api_key=None, app_insights_app_id=None):
app_insights_api_key=None, app_insights_app_id=None, icon_url=None):
bot = client.bots.get(
resource_group_name=resource_group_name,
resource_name=resource_name
Expand All @@ -658,6 +666,7 @@ def update(client, resource_group_name, resource_name, endpoint=None, descriptio
bot_props.description = description if description else bot_props.description
bot_props.display_name = display_name if display_name else bot_props.display_name
bot_props.endpoint = endpoint if endpoint else bot_props.endpoint
bot_props.icon_url = icon_url if icon_url else bot_props.icon_url

bot_props.developer_app_insight_key = app_insights_key if app_insights_key else bot_props.developer_app_insight_key
bot_props.developer_app_insights_application_id = app_insights_app_id if app_insights_app_id \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------


class NameAvailability:
@staticmethod
def check_name_availability(client, resource_name, bot_type):
return client.bots.get_check_name_availability(resource_name, bot_type)

@staticmethod
def check_enterprise_channel_name_availability(client, channel_name):
return client.enterprise_channels.check_name_availability(channel_name)
Loading