allow deployment to non-default subscriptions (#774)

This commit is contained in:
bmc-msft
2021-04-06 13:06:35 -04:00
committed by GitHub
parent a33aba26f2
commit 88af5f4dd3
2 changed files with 89 additions and 38 deletions

View File

@ -122,7 +122,9 @@ class Client:
log_service_principal: bool, log_service_principal: bool,
multi_tenant_domain: str, multi_tenant_domain: str,
upgrade: bool, upgrade: bool,
subscription_id: Optional[str],
): ):
self.subscription_id = subscription_id
self.resource_group = resource_group self.resource_group = resource_group
self.arm_template = arm_template self.arm_template = arm_template
self.location = location self.location = location
@ -171,11 +173,16 @@ class Client:
self.workbook_data = json.load(f) self.workbook_data = json.load(f)
def get_subscription_id(self) -> str: def get_subscription_id(self) -> str:
if self.subscription_id:
return self.subscription_id
profile = get_cli_profile() profile = get_cli_profile()
return cast(str, profile.get_subscription_id()) self.subscription_id = cast(str, profile.get_subscription_id())
return self.subscription_id
def get_location_display_name(self) -> str: def get_location_display_name(self) -> str:
location_client = get_client_from_cli_profile(SubscriptionClient) location_client = get_client_from_cli_profile(
SubscriptionClient, subscription_id=self.get_subscription_id()
)
locations = location_client.subscriptions.list_locations( locations = location_client.subscriptions.list_locations(
self.get_subscription_id() self.get_subscription_id()
) )
@ -194,7 +201,9 @@ class Client:
with open(self.arm_template, "r") as handle: with open(self.arm_template, "r") as handle:
arm = json.load(handle) arm = json.load(handle)
client = get_client_from_cli_profile(ResourceManagementClient) client = get_client_from_cli_profile(
ResourceManagementClient, subscription_id=self.get_subscription_id()
)
providers = {x.namespace: x for x in client.providers.list()} providers = {x.namespace: x for x in client.providers.list()}
unsupported = [] unsupported = []
@ -233,7 +242,7 @@ class Client:
sys.exit(1) sys.exit(1)
def create_password(self, object_id: UUID) -> Tuple[str, str]: def create_password(self, object_id: UUID) -> Tuple[str, str]:
return add_application_password(object_id) return add_application_password(object_id, self.get_subscription_id())
def setup_rbac(self) -> None: def setup_rbac(self) -> None:
""" """
@ -245,7 +254,9 @@ class Client:
logger.info("using existing client application") logger.info("using existing client application")
return return
client = get_client_from_cli_profile(GraphRbacManagementClient) client = get_client_from_cli_profile(
GraphRbacManagementClient, subscription_id=self.get_subscription_id()
)
logger.info("checking if RBAC already exists") logger.info("checking if RBAC already exists")
try: try:
@ -400,7 +411,10 @@ class Client:
"subscription, creating a new one" "subscription, creating a new one"
) )
app_info = register_application( app_info = register_application(
"onefuzz-cli", self.application_name, OnefuzzAppRole.CliClient "onefuzz-cli",
self.application_name,
OnefuzzAppRole.CliClient,
self.get_subscription_id(),
) )
if self.multi_tenant_domain: if self.multi_tenant_domain:
authority = COMMON_AUTHORITY authority = COMMON_AUTHORITY
@ -429,7 +443,9 @@ class Client:
with open(self.arm_template, "r") as template_handle: with open(self.arm_template, "r") as template_handle:
template = json.load(template_handle) template = json.load(template_handle)
client = get_client_from_cli_profile(ResourceManagementClient) client = get_client_from_cli_profile(
ResourceManagementClient, subscription_id=self.get_subscription_id()
)
client.resource_groups.create_or_update( client.resource_groups.create_or_update(
self.resource_group, {"location": self.location} self.resource_group, {"location": self.location}
) )
@ -512,6 +528,7 @@ class Client:
assign_scaleset_role( assign_scaleset_role(
self.application_name, self.application_name,
self.results["deploy"]["scaleset-identity"]["value"], self.results["deploy"]["scaleset-identity"]["value"],
self.get_subscription_id(),
) )
def apply_migrations(self) -> None: def apply_migrations(self) -> None:
@ -548,7 +565,9 @@ class Client:
logger.info("creating eventgrid subscription") logger.info("creating eventgrid subscription")
src_resource_id = self.results["deploy"]["fuzz-storage"]["value"] src_resource_id = self.results["deploy"]["fuzz-storage"]["value"]
dst_resource_id = self.results["deploy"]["func-storage"]["value"] dst_resource_id = self.results["deploy"]["func-storage"]["value"]
client = get_client_from_cli_profile(StorageManagementClient) client = get_client_from_cli_profile(
StorageManagementClient, subscription_id=self.get_subscription_id()
)
event_subscription_info = EventSubscription( event_subscription_info = EventSubscription(
destination=StorageQueueEventSubscriptionDestination( destination=StorageQueueEventSubscriptionDestination(
resource_id=dst_resource_id, queue_name="file-changes" resource_id=dst_resource_id, queue_name="file-changes"
@ -565,7 +584,9 @@ class Client:
), ),
) )
client = get_client_from_cli_profile(EventGridManagementClient) client = get_client_from_cli_profile(
EventGridManagementClient, subscription_id=self.get_subscription_id()
)
result = client.event_subscriptions.create_or_update( result = client.event_subscriptions.create_or_update(
src_resource_id, "onefuzz1", event_subscription_info src_resource_id, "onefuzz1", event_subscription_info
).result() ).result()
@ -639,7 +660,8 @@ class Client:
) )
app_insight_client = get_client_from_cli_profile( app_insight_client = get_client_from_cli_profile(
ApplicationInsightsManagementClient ApplicationInsightsManagementClient,
subscription_id=self.get_subscription_id(),
) )
to_delete = [] to_delete = []
@ -829,7 +851,7 @@ class Client:
def update_registration(self) -> None: def update_registration(self) -> None:
if not self.create_registration: if not self.create_registration:
return return
update_pool_registration(self.application_name) update_pool_registration(self.application_name, self.get_subscription_id())
def done(self) -> None: def done(self) -> None:
logger.info(TELEMETRY_NOTICE) logger.info(TELEMETRY_NOTICE)
@ -967,6 +989,10 @@ def main() -> None:
default=None, default=None,
help="enable multi-tenant authentication with this tenant domain", help="enable multi-tenant authentication with this tenant domain",
) )
parser.add_argument(
"--subscription_id",
type=str,
)
args = parser.parse_args() args = parser.parse_args()
if shutil.which("func") is None: if shutil.which("func") is None:
@ -992,6 +1018,7 @@ def main() -> None:
log_service_principal=args.log_service_principal, log_service_principal=args.log_service_principal,
multi_tenant_domain=args.multi_tenant_domain, multi_tenant_domain=args.multi_tenant_domain,
upgrade=args.upgrade, upgrade=args.upgrade,
subscription_id=args.subscription_id,
) )
if args.verbose: if args.verbose:
level = logging.DEBUG level = logging.DEBUG

View File

@ -80,9 +80,10 @@ def query_microsoft_graph(
) )
def get_graph_client() -> GraphRbacManagementClient: def get_graph_client(subscription_id: str) -> GraphRbacManagementClient:
client: GraphRbacManagementClient = get_client_from_cli_profile( client: GraphRbacManagementClient = get_client_from_cli_profile(
GraphRbacManagementClient GraphRbacManagementClient,
subscription_id=subscription_id,
) )
return client return client
@ -99,10 +100,13 @@ class OnefuzzAppRole(Enum):
def register_application( def register_application(
registration_name: str, onefuzz_instance_name: str, approle: OnefuzzAppRole registration_name: str,
onefuzz_instance_name: str,
approle: OnefuzzAppRole,
subscription_id: str,
) -> ApplicationInfo: ) -> ApplicationInfo:
logger.info("retrieving the application registration %s" % registration_name) logger.info("retrieving the application registration %s" % registration_name)
client = get_graph_client() client = get_graph_client(subscription_id)
apps: List[Application] = list( apps: List[Application] = list(
client.applications.list(filter="displayName eq '%s'" % registration_name) client.applications.list(filter="displayName eq '%s'" % registration_name)
) )
@ -110,7 +114,7 @@ def register_application(
if len(apps) == 0: if len(apps) == 0:
logger.info("No existing registration found. creating a new one") logger.info("No existing registration found. creating a new one")
app = create_application_registration( app = create_application_registration(
onefuzz_instance_name, registration_name, approle onefuzz_instance_name, registration_name, approle, subscription_id
) )
else: else:
app = apps[0] app = apps[0]
@ -136,7 +140,7 @@ def register_application(
if app.app_id not in [app.app_id for app in pre_authorized_applications]: if app.app_id not in [app.app_id for app in pre_authorized_applications]:
authorize_application(UUID(app.app_id), UUID(onefuzz_app.app_id)) authorize_application(UUID(app.app_id), UUID(onefuzz_app.app_id))
password = create_application_credential(registration_name) password = create_application_credential(registration_name, subscription_id)
return ApplicationInfo( return ApplicationInfo(
client_id=app.app_id, client_id=app.app_id,
@ -145,27 +149,27 @@ def register_application(
) )
def create_application_credential(application_name: str) -> str: def create_application_credential(application_name: str, subscription_id: str) -> str:
""" Add a new password to the application registration """ """ Add a new password to the application registration """
logger.info("creating application credential for '%s'" % application_name) logger.info("creating application credential for '%s'" % application_name)
client = get_graph_client() client = get_graph_client(subscription_id)
apps: List[Application] = list( apps: List[Application] = list(
client.applications.list(filter="displayName eq '%s'" % application_name) client.applications.list(filter="displayName eq '%s'" % application_name)
) )
app: Application = apps[0] app: Application = apps[0]
(_, password) = add_application_password(app.object_id) (_, password) = add_application_password(app.object_id, subscription_id)
return str(password) return str(password)
def create_application_registration( def create_application_registration(
onefuzz_instance_name: str, name: str, approle: OnefuzzAppRole onefuzz_instance_name: str, name: str, approle: OnefuzzAppRole, subscription_id: str
) -> Application: ) -> Application:
""" Create an application registration """ """ Create an application registration """
client = get_graph_client() client = get_graph_client(subscription_id)
apps: List[Application] = list( apps: List[Application] = list(
client.applications.list(filter="displayName eq '%s'" % onefuzz_instance_name) client.applications.list(filter="displayName eq '%s'" % onefuzz_instance_name)
) )
@ -207,7 +211,6 @@ def create_application_registration(
try: try:
time.sleep(5) time.sleep(5)
client = get_graph_client()
update_param = ApplicationUpdateParameters( update_param = ApplicationUpdateParameters(
reply_urls=["https://%s.azurewebsites.net" % onefuzz_instance_name] reply_urls=["https://%s.azurewebsites.net" % onefuzz_instance_name]
) )
@ -221,7 +224,9 @@ def create_application_registration(
return registered_app return registered_app
def add_application_password(app_object_id: UUID) -> Tuple[str, str]: def add_application_password(
app_object_id: UUID, subscription_id: str
) -> Tuple[str, str]:
# Work-around the race condition where the app is created but passwords cannot # Work-around the race condition where the app is created but passwords cannot
# be created yet. # be created yet.
@ -234,7 +239,7 @@ def add_application_password(app_object_id: UUID) -> Tuple[str, str]:
if count > 1: if count > 1:
logging.info("retrying app password creation") logging.info("retrying app password creation")
try: try:
password = add_application_password_impl(app_object_id) password = add_application_password_impl(app_object_id, subscription_id)
logging.info("app password created") logging.info("app password created")
return password return password
except GraphQueryError as err: except GraphQueryError as err:
@ -255,11 +260,13 @@ def add_application_password(app_object_id: UUID) -> Tuple[str, str]:
raise Exception("unable to create password") raise Exception("unable to create password")
def add_application_password_legacy(app_object_id: UUID) -> Tuple[str, str]: def add_application_password_legacy(
app_object_id: UUID, subscription_id: str
) -> Tuple[str, str]:
key = str(uuid4()) key = str(uuid4())
password = str(uuid4()) password = str(uuid4())
client = get_graph_client() client = get_graph_client(subscription_id)
password_cred = [ password_cred = [
PasswordCredential( PasswordCredential(
start_date="%s" % datetime.now(TZ_UTC).strftime("%Y-%m-%dT%H:%M.%fZ"), start_date="%s" % datetime.now(TZ_UTC).strftime("%Y-%m-%dT%H:%M.%fZ"),
@ -275,7 +282,9 @@ def add_application_password_legacy(app_object_id: UUID) -> Tuple[str, str]:
return (key, password) return (key, password)
def add_application_password_impl(app_object_id: UUID) -> Tuple[str, str]: def add_application_password_impl(
app_object_id: UUID, subscription_id: str
) -> Tuple[str, str]:
key = uuid4() key = uuid4()
password_request = { password_request = {
"passwordCredential": { "passwordCredential": {
@ -296,7 +305,7 @@ def add_application_password_impl(app_object_id: UUID) -> Tuple[str, str]:
) )
return (str(key), password["secretText"]) return (str(key), password["secretText"])
except adal.AdalError: except adal.AdalError:
return add_application_password_legacy(app_object_id) return add_application_password_legacy(app_object_id, subscription_id)
def get_application(app_id: UUID) -> Optional[Any]: def get_application(app_id: UUID) -> Optional[Any]:
@ -359,13 +368,17 @@ def authorize_application(
def create_and_display_registration( def create_and_display_registration(
onefuzz_instance_name: str, registration_name: str, approle: OnefuzzAppRole onefuzz_instance_name: str,
registration_name: str,
approle: OnefuzzAppRole,
subscription_id: str,
) -> None: ) -> None:
logger.info("Updating application registration") logger.info("Updating application registration")
application_info = register_application( application_info = register_application(
registration_name=registration_name, registration_name=registration_name,
onefuzz_instance_name=onefuzz_instance_name, onefuzz_instance_name=onefuzz_instance_name,
approle=approle, approle=approle,
subscription_id=subscription_id,
) )
logger.info("Registration complete") logger.info("Registration complete")
logger.info("These generated credentials are valid for a year") logger.info("These generated credentials are valid for a year")
@ -373,19 +386,20 @@ def create_and_display_registration(
logger.info("client_secret: %s" % application_info.client_secret) logger.info("client_secret: %s" % application_info.client_secret)
def update_pool_registration(onefuzz_instance_name: str) -> None: def update_pool_registration(onefuzz_instance_name: str, subscription_id: str) -> None:
create_and_display_registration( create_and_display_registration(
onefuzz_instance_name, onefuzz_instance_name,
"%s_pool" % onefuzz_instance_name, "%s_pool" % onefuzz_instance_name,
OnefuzzAppRole.ManagedNode, OnefuzzAppRole.ManagedNode,
subscription_id,
) )
def assign_scaleset_role_manually( def assign_scaleset_role_manually(
onefuzz_instance_name: str, scaleset_name: str onefuzz_instance_name: str, scaleset_name: str, subscription_id: str
) -> None: ) -> None:
client = get_graph_client() client = get_graph_client(subscription_id)
apps: List[Application] = list( apps: List[Application] = list(
client.applications.list(filter="displayName eq '%s'" % onefuzz_instance_name) client.applications.list(filter="displayName eq '%s'" % onefuzz_instance_name)
) )
@ -441,7 +455,9 @@ def assign_scaleset_role_manually(
) )
def assign_scaleset_role(onefuzz_instance_name: str, scaleset_name: str) -> None: def assign_scaleset_role(
onefuzz_instance_name: str, scaleset_name: str, subscription_id: str
) -> None:
""" """
Allows the nodes in the scaleset to access the service by assigning Allows the nodes in the scaleset to access the service by assigning
their managed identity to the ManagedNode Role their managed identity to the ManagedNode Role
@ -513,7 +529,9 @@ def assign_scaleset_role(onefuzz_instance_name: str, scaleset_name: str) -> None
}, },
) )
except adal.AdalError: except adal.AdalError:
assign_scaleset_role_manually(onefuzz_instance_name, scaleset_name) assign_scaleset_role_manually(
onefuzz_instance_name, scaleset_name, subscription_id
)
def set_app_audience(objectId: str, audience: str) -> None: def set_app_audience(objectId: str, audience: str) -> None:
@ -555,6 +573,7 @@ def main() -> None:
parent_parser.add_argument( parent_parser.add_argument(
"onefuzz_instance", help="the name of the onefuzz instance" "onefuzz_instance", help="the name of the onefuzz instance"
) )
parent_parser.add_argument("subscription_id")
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
formatter_class=formatter, formatter_class=formatter,
@ -593,14 +612,19 @@ def main() -> None:
onefuzz_instance_name = args.onefuzz_instance onefuzz_instance_name = args.onefuzz_instance
if args.command == "update_pool_registration": if args.command == "update_pool_registration":
update_pool_registration(onefuzz_instance_name) update_pool_registration(onefuzz_instance_name, args.subscription_id)
elif args.command == "create_cli_registration": elif args.command == "create_cli_registration":
registration_name = args.registration_name or ("%s_cli" % onefuzz_instance_name) registration_name = args.registration_name or ("%s_cli" % onefuzz_instance_name)
create_and_display_registration( create_and_display_registration(
onefuzz_instance_name, registration_name, OnefuzzAppRole.CliClient onefuzz_instance_name,
registration_name,
OnefuzzAppRole.CliClient,
args.subscription_id,
) )
elif args.command == "assign_scaleset_role": elif args.command == "assign_scaleset_role":
assign_scaleset_role(onefuzz_instance_name, args.scaleset_name) assign_scaleset_role(
onefuzz_instance_name, args.scaleset_name, args.subscription_id
)
else: else:
raise Exception("invalid arguments") raise Exception("invalid arguments")