From 6036250086a8a2a799e10e418b0e798f3291e36b Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 10 Sep 2020 00:13:36 +0200 Subject: [PATCH 01/17] Add e2e exemple to schedule asset inventory --- .../backend.tf.sample | 23 ++++ .../main.tf | 127 ++++++++++++++++++ .../variables.tf | 53 ++++++++ 3 files changed, 203 insertions(+) create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/main.tf create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/variables.tf diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample b/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample new file mode 100644 index 000000000..61572d61a --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/backend.tf.sample @@ -0,0 +1,23 @@ +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# set a valid bucket below and rename this file to backend.tf + +terraform { + backend "gcs" { + bucket = "" + prefix = "fabric/operations/inventory" + } +} + diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf new file mode 100644 index 000000000..06372c04d --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -0,0 +1,127 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module "project" { + source = "../../modules/project" + name = var.project_id + parent = "folders/572946148602" + billing_account = "0074F6-6FDF57-1B2DD0" + project_create = var.project_create + iam_additive_bindings = { + "user:admin@caggioland.com" = [ + "roles/owner" + ] + } + services = [ + "bigquery.googleapis.com", + "cloudasset.googleapis.com", + "compute.googleapis.com", + "cloudfunctions.googleapis.com", + "cloudbuild.googleapis.com", + "cloudscheduler.googleapis.com", + "pubsub.googleapis.com" + ] +} + +module "service-account" { + source = "../../modules/iam-service-accounts" + project_id = module.project.project_id + names = ["${var.name}-cf"] + iam_project_roles = { + (module.project.name) = [ + "roles/cloudasset.viewer", + "roles/cloudfunctions.invoker" + ] + } +} + +module "pubsub" { + source = "../../modules/pubsub" + project_id = module.project.project_id + name = var.name + subscriptions = { + "${var.name}-default" = null + } + # the Cloud Scheduler robot service account already has pubsub.topics.publish + # at the project level via roles/cloudscheduler.serviceAgent +} + +module "cf" { + source = "../../modules/cloud-function" + project_id = module.project.project_id + name = var.name + bucket_name = "${var.name}-${random_pet.random.id}" + bucket_config = { + location = var.region + lifecycle_delete_age = null + } + bundle_config = { + source_dir = "cf" + output_path = var.bundle_path + } + service_account = module.service-account.email + iam_roles = ["roles/cloudfunctions.invoker"] + iam_members = { + "roles/cloudfunctions.invoker" = ["serviceAccount:${module.service-account.email}"] + } + trigger_config = { + event = "google.pubsub.topic.publish" + resource = module.pubsub.topic.id + retry = null + } +} + +resource "google_app_engine_application" "app" { + project = module.project.project_id + location_id = "europe-west" +} + +resource "google_cloud_scheduler_job" "job" { + project = module.project.project_id + region = var.region + name = "test-job" + description = "test http job" + schedule = "* 9 * * 1" + time_zone = "Etc/UTC" + attempt_deadline = "320s" + + pubsub_target { + attributes = {} + topic_name = module.pubsub.topic.id + data = base64encode(jsonencode({ + organization = var.cai_config.organization + bq_project = var.project_id + bq_dataset = var.cai_config.bq_dataset + bq_table = var.cai_config.bq_table + })) + } +} + +module "bigquery-dataset" { + source = "../../modules/bigquery-dataset" + project_id = module.project.project_id + id = var.cai_config.bq_dataset + access_roles = { + owner = { role = "OWNER", type = "user_by_email" } + } + access_identities = { + owner = module.service-account.email + } +} + +resource "random_pet" "random" { + length = 1 +} diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf new file mode 100644 index 000000000..85311ee1e --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -0,0 +1,53 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "bundle_path" { + description = "Path used to write the intermediate Cloud Function code bundle." + type = string + default = "./bundle.zip" +} + +variable "name" { + description = "Arbitrary string used to name created resources." + type = string + default = "asset-inventory" +} + +variable "project_create" { + description = "Create project instead ofusing an existing one." + type = bool + default = false +} + +variable "project_id" { + description = "Project id that references existing project." + type = string +} + +variable "region" { + description = "Compute region used in the example." + type = string + default = "europe-west1" +} + +variable "cai_config" { + description = "Cloud Asset inventory export config." + type = object({ + organization = string + bq_dataset = string + bq_table = string + }) +} From 58b4bf383704816603489e95085b4ecf12dbec2b Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 10 Sep 2020 00:15:52 +0200 Subject: [PATCH 02/17] Add Cloud Function --- .../cf/main.py | 106 ++++++++++++++++++ .../cf/requirements.txt | 4 + 2 files changed, 110 insertions(+) create mode 100755 cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/cf/requirements.txt diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py new file mode 100755 index 000000000..63a964d5a --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py @@ -0,0 +1,106 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Cloud Function module to export data for a given day. + +This module is designed to be plugged in a Cloud Function, attached to Cloud +Scheduler trigger to create a Cloud Asset Inventory Export to BigQuery. + +''' + +import base64 +import datetime +import json +import logging +import os +import warnings + +import click + +from google.api_core.exceptions import GoogleAPIError +from google.cloud import asset_v1 + +import googleapiclient.discovery +import googleapiclient.errors + +def _configure_logging(verbose=True): + """Basic logging configuration. + Args: + verbose: enable verbose logging + """ + level = logging.DEBUG if verbose else logging.INFO + logging.basicConfig(level=level) + warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) + +@click.command() +@click.option('--organization', required=True, + help='Organization ID') +@click.option('--bq-project', required=True, + help='Bigquery project to use.') +@click.option('--bq-dataset', required=True, + help='Bigquery dataset to use.') +@click.option('--bq-table', required=True, + help='Bigquery table name to use.') +@click.option('--read-time', required=True, + help='Day to take an asset snapshot in the format \'YYYYMMDD\'. \ + If not specified run for the current day. Export will run for \ + the midnight of the specified day.') +@click.option('--verbose', is_flag=True, help='Verbose output') +def main_cli(organization=None, bq_project=None, bq_dataset=None, bq_table=None, read_time=None, verbose=False): + """Trigger Cloud Asset inventory export to Bigquery. Data will + be stored in the dataset specified on a dated table with the name + specified. + """ + try: + _main(organization, bq_project, bq_dataset, bq_table) + except RuntimeError: + logging.exception('exception raised') + + +def main(event, context): + """Cloud Function entry point.""" + try: + _main(**event) + # uncomment once https://issuetracker.google.com/issues/155215191 is fixed + # except RuntimeError: + # raise + except Exception: + logging.exception('exception in cloud function entry point') + + +def _main(organization=None, bq_project=None, bq_dataset=None, bq_table=None, read_time=None, verbose=False): + """Module entry point used by cli and cloud function wrappers.""" + + if not read_time: + read_time = datetime.date.today().strftime("%Y%m%d") + + client = asset_v1.AssetServiceClient() + parent = "organizations/%s" % organization + content_type = asset_v1.ContentType.RESOURCE + output_config = asset_v1.OutputConfig() + output_config.bigquery_destination.dataset = "projects/%s/datasets/%s" % (bq_project, bq_dataset) + output_config.bigquery_destination.table = "%s_%s" % (bq_table, read_time) + output_config.bigquery_destination.force = True + response = client.export_assets( + request={ + "parent": parent, + "read_time": datetime.datetime.strptime(read_time, '%Y%m%d'), + "content_type": content_type, + "output_config": output_config + } + ) + + +if __name__ == '__main__': + main_cli() diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cf/requirements.txt b/cloud-operations/scheduled-asset-inventory-export-bq/cf/requirements.txt new file mode 100644 index 000000000..6e893cc33 --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cf/requirements.txt @@ -0,0 +1,4 @@ +Click>=7.0 +google-api-python-client>=1.10.1 +google-cloud-monitoring>=1.1.0 +google-cloud-asset \ No newline at end of file From 4313cd28042d7ef5388822fad7df88bd836045c1 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 10 Sep 2020 00:23:25 +0200 Subject: [PATCH 03/17] Add diagram --- .../diagram.png | Bin 0 -> 33936 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/diagram.png diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png b/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..1b2f71aa5d956a4b2d9cd2772eec2e63d735f309 GIT binary patch literal 33936 zcmdpd^;2A1)aAt+2ol^axDy}lQ001OOiLXik01XcS5O5&K_ZboO5C{MOIeVyR zI4K#rlGr)enps$xk~q2BnUa{gS(pKU+j2#+b=+<<($}|Z41UNi2%=!}ZqP>e;C)_c zskw*CLfiK?OjRXiGD{zc7i7HEWC+Po;u@i`KHbHo_P}6t~wgC0!+Si4Jdod zvpqXc2ainVRNj9Dzigt9fR(#`du0B7+gkC9q4-X(;&2xy<<)tMDdg@=nbmXOF^cso zR>xs;PRc`GOt{nf+%f9%0NPvr6-@h__VV;ed?POF033QH%?O5Tcm;DRflb@JDzbd_ zn!Q95x;8G5NPX5Oz>ZNz+^@@P<(Uz8oO!=Yd_Ox8>sVEOsd7f%nr%)xz#+ghUlp{m zAMCH0l}K&Rim|L^o0WXca9s}6s59TnxSrUV~C+$vPu(WutR@HOw#b5XkQ0qEc~J@Ms5d`Ch9827P2wF1W3+-!3O$ycSw{e_Hc>Ee?xc3U4ru6$(fDM6vj96`HeIZ`@ zJ7-C%tfFB(-{M=|xSq#Z#XaLo*lGfqYMRqWMcmH~-{uQ-MOA|*V#S4^Rt`meMzQRV z8P-mjR1ROq$0n+4IycNG)9V_KBYC^5S>H2g-!IPAVv10fr>87eAJK40ZOx~U5oxT!h%~@)S?`6sew;oG!oR{+jE)0b`gjHOE(7<`ocFon-2vcuDwCE08U-~ zXRZo$CyrpDQ`I2Oq8bJ7;LQSS8}VZfsZl`P-;w zN4VYM1V?huud9*I*$~%(sVeZLMGPxO%7!7cbyh_j|620{*9?P=&`K2GBPf2C+We`Y zoWzbqS-93QT1)!4F+Rd)e^G3|4VWToVf^vIK5`RUd7^JAUDhlcF63>T`#BPO=$C|j z8Y%y&eTEI936h>v%o5mYG0|Db`9#)@LYaL0>XKvCg)%GPNkKk63O>##S9qXGp) z6qYKa%0-#UpABV>r)zYnIt;%iEntBUU){>lZ5u+e!eNulXo1E&Edh;R#%rAjUTv+- zQjAJ}_*Sn}-LR$Ch8dm5>2;i-J)cIuZ6U*}HQOOeVRblSds-z-4QQH)-E;e`_G!Vd1Fmq zoto>|b~bVf7&USiXYN?$#Wm>A>te&4r|=s3w5!{@sSfG`wmZpN>X`ruQBr_R^U%`!=aO zA8+~<6^4vWc`6*a>BY}l)JH1x{n^{Lji}g2CJ$e7516G4Kb3n=38@f|G9o0VXnl>t zJL|Jr9i5Kt2@TBAo_?YX+5)dn*10z3w)dR5Z5K-lA#8jpvVi#12Zj9Gq56CM3Sk4m z4`fhD`YpNBOBm@4RIUP?4{?1k0>jYQ&(9-`p7JZkW&0Qqbn^LR=+b`P-|AV|10_4$ zKaImJ6*U+?+Gp=uxefnB;~I(?)n~OdmhGdvb!T6MRK4E%OmS)GJQgxw{YG71ha4s1 z8EGZD|2;hNFQ4Prh$12N<7p_Lns$*axi23ZD=MkWa%@G=OjB!T3Q3}y#l?SI%vZo= zbm;bx^Ct33Z5moa!%1=t^zmY84eiaec@B2+2z;bW=7`i{*BS70Tw9j%WLs=Zs`xGr z6>Js6I19He9uR1#(NZ8+UhMEm*M!(d2KsaS9z=m1-zzC$6MiP1Sp)YJ>MN6z9HAsG z&hZgD{<5(9lcjqT|BN1pu@pts;2XDPceLzrXc!@B*DBs(iqSLk?Hy`g1ku@v={I1n zxL_TrTU#UV8leuqU)NH64@69)0~pI+q{`MY zfcxq@Ru6k%fr9a2HTUP@_~1qui)ZG3Al$eMF8pWJ-uIU)glauh#eq7?LOxz)rL||a zP59QDgiH2Kvn?0jG@n{-8d8VO61-h(QH>_`mUV*B5%sS$h)-maa5>n|E*T;x;^z@! zvs=^2LsEN4z4TenO>@*y(Jk|3W8XX%snQ~=eY77^$fOSmTG);WSaGT3VL=$k(y?lst>@odeE;i<58O0?`kENwyVW`H&zAd?3TzZtiWb#Fg z+t&V%6y~31DqlmcA|@vL%s;4T8r@{%AqomTuu|0@^K_~Dg~KLx_+Nzk}LpK?QDl@PO8`ReIf zH=x5MSjNiqv|2cwzW|i{PpgqijC_jS>uxZvw=&jY2TZG4M<@G){S%O1qCSkjxrvHH zqi~lt*~Y5{zO$9h^A+GOc8F;hg+P4Qg|J4_;$MNce;(N#Me*-5@OBcKjsSp&{?8Kv zNXx)^pM-Oglof^Bg@?z&Vhwwm9|HgqK=P}Qirez(nyVK<{}SAVxtVL#iCl+tlF4x6 zs;4bZ0MfvspGDzMR4Kn0R1#A7_APG_$-{9c=J_04DA*l;&vVfvilthQ^P;jgUe(%VLz7Vn~(p-;0eJ=B34(ti=-!A6Z9oSn6mbaQWMJoG+`KOp^rX;Scl$b}uru5>K8 z)BE=67T<3H_$Sn$oNE^Rq1r~9hNn}3pcNP~NA)U`50=+RzFtbL#~zZ39*ildq|kcm z|7Af=HF`p{P({1rx9@^XP8~Wl6c9rlIM`LscUploV57DVPYP^aKW56AtE$Qx=ZzAl zto!}Xsx0`pH~dV^9*EnBv<>C4=b1SUkQ`kG7Psqza73-3?LPM(&Y1>t|Rwmyw( zH`0WCw%iTbG!W;^#Y!ZH2XKr7QKd>ybrv*Fx(I=Z8)6AiEIGVjJwMyOapiftSxq6E zKksy}wDVk^rz_~`O=Lme4L64mh&@}%snQ+(Q<>|rgAes;tGQYL+XMKaxjj4Z(C;Ng z4+ps&bf7d^Z&)znKsGFPr{s25lacm0FX%Y;BuJV&CVV8#uP#2cjeenHTfCggI**PH z=R3Qnp{8H1w!bc9SW%IMA~nj*V`{e>39K>35QT>hAWnYUbz;}!|87P?oZcPq~)|A18$6R_F{k*7YQTXaUS@_&5w(uT2QkRSgm#;v?y<;!Z*h z`c`W?wzuX(B|p371C+B1<>ugvmm30K0qqy#(|&a0x1L2KJ6@5P55EMfC^y8S%q z^&xm;BQ;|+T&dM^JT2psdmUrTgG}XDM4RY?m4eZy!U6*h!GF&1Z!0^a+{%Hs^TE9> z@V-#T!OAk&TrH-%f@2mUyFs*E8zz^_Bfp5|S?0dT9EAgs^ zu5u^4#zy$dsYmwe%qCi$%nTl6!)fBFylW4`zgKqpzs)2`Vghd-PfKB8Ba6kw_y_u| zZc~614ZxK-5zYVdEI82P`{i}lKlv9t3JN=QSicpe`Ij=k>2F+ly6FF6&Y{;ZY@qPE z$I2QQsYCGavmGRmje2n&i=rbrmR)d$8g=vM`d5%26=Se~7i8yt&ebKXMzheD&)|I8 z*1(v&M;PhOmmVS$d=96JRqo`=9kQSvgXWz(ZD-x{>e=kI!@P+oVGsyHxBaKm%q^na zZFa_W(}u2wqT*7O{4t;F+k{a~qYM=Y3Ca6m^8T&a%hayAcdOdfUPcPO8ye}Mf*=v5K*8;Z!SIs5R%0YGJeOb`pA?B^#Y48P z<*wwcIPr)wniLGpyGnG9w4gh?RP`;*<$2|!u!&}6S(!kZzJj(k-Kc{>YMx6~EO|+7 zDJ@gi(4K?qqKO=hsmvr3b!B;J9C@fxDRo#b^L={zx5^&nA zU)dGatSl|%_TpODyMu0}{}(H!s)X*JkU$g--wOgb{f%I!cq57ZrCMCCLc#?@VK&E| zzlVIFkO8$6FfwdL5|Z&i??;kaCQF|`cy*>NYfU^daOx3bn0(h&L}qhy#i4?H^B})J zgd_G&OLf*zG#PO7=&E0_=_Dm3?JPhW!x{aOa658xJ~uxv9hYypybEt^+y0ss5^^Cp zLlW4$_LtA|zT;`YQy7$a)UmyDpzHof_4YDj$?~=n8yqFzVQ6^iN$eG8Pc5CRGG8yv z^vmbAy`=|PyzP)|^y--XM3?ctJQZ71RMb*ykdD)tKmd3yZMAtB;M{m&zIbyUux-0s zwwNu1U9ticl(jt%_Iz`_b3>QrvZTs+cb@*VT_;~!iU1@gsxmRT(XrBL z$B|b}`Yzbo{#~GwjbkPy`3Ij3IS$j7Ne=iR!g6GpQ9t-WDUc{RR;ZemWcd>;p?!dq zBpH+Ql3CPJc6cOj-wS&=1rS$cyx4* ziD*U(T%PmVbU7bx3PrGFO*U;%PcxRaMYBor{(tzKu@#x;Dl|`Yx8+`kHFidUfDctGeD+sf($48jw1&dF#H|wKcD4wr>bQ1<$$a9-(v-;6T${jM1p(j@FYea%6^5)b2IwWz{is75;$Sd0M}75Oh<9?Lwdm|d5Z?S*=Dtn!|u9BW!&FNrbE&Z)jNY~k)baeEc(NQ`*-;=f6?YM^JLd^t5EEYk@4RSa8&bRO1uAWU%p!zQPtacd4c<`w`b22 ztL~tSC$9*Wws9A!M0??(CTZWHp)o0m5b%M(>-#xJ-7e*>(|3QVSo6GNcr-g2=NyNd zkqj2OmJdQjdEO&Nqfn-dd>d!rbYLWxOyhB%xn}ATgYT(A_>ZWn@Qp=7$W@uF@Y$Nz zeSHb&~|sqj*Ik3{6pKdYm;ULmh9{d__pP3y$wHuQ8P}hTxZY9$_pGI z59+=_EF4Sa(>9NAbeT_7A8R>{+c^M+YTFHG&2$$)IsaUjg=P9NNHn;%_MMMOzUmn& zN?K^PS@QS+Bt^?o8=Fpl7h6URn>HL5&Z!GpD_IA~HCqoVS9*JYcWO7*)|W>BM?AVF zW~L{tf12c%D>yfbEqIJ&ROerhylsE^BK`IF`48~je4py@2`H8Kt8a%gj;%uU^hJdX zLvlJ^X%-S>J_-F zmPVDRMyn~)JA+XcCFewAUv8eItCr4$!g9-3nuAbrV%8VTzNh7q1XiyEz}}p+IfE-1 zR$TQQZ!uL>+0!cJ)0p`$s(@~s@Q8>Me(S3VUC+|eQbYsjt7^DU!OA5UlZvS`@aI$5;-@N&A9P+u*gtj=?w& zY!I1Ej`$y>l$5GOG`V?Q&X(G2-!2SAwEtXNG@Y8+{8f~)aPT5H(3y844eU3v0IP$< zs{Ys3NjvAG=S@Xu}M3Qx>X@PvBO{&X z^1kzI3?2IaPzM|`0V+N^19W|55R@M(v(IU9#;2Ba4qI8d<9SiOPF<@_f5!<-O@5!5 z>fvu>&?>Ul+yxwi-$iGgAyG|`EbsTx673xSt$1?u1OAo^d}?^<`f#%&4w*UEXT&r~)l*Y3sNwxGG!^CLmYi(9YXjqfsCm;k=;(PCt)-=^^VWLG?+J2f zix!w1DwY^%sXQs6k(7Vi$cq+u8VZ_a3<-en{@UW429 znEp>jqZB}ps0@i7Qf1ii27wvBJ<$M7e^ao;w=$NySTn9l_|5>@tE|6o9psWkRrjGq zS>waptBZ$m6p~4%qOtEh`wE47|K$SY7h`JryvQOPB5fuE=l6n}s=35nJGc8T0sViI zb!`Zk|B-21`;~&qBze;-FF;pStbjpLt=s)BwnwB!UrKr2B}-uQV$9}y88KDI)PC&Z zX`&fty0mloT5Tm7?EC_6EY8W%+JdTFBy&3ZPQK)#ic(Ue{11n-?_j1QBx!2j>BK9A!Q!T ztuA&unc#!XBV}NpH)^zFbciNURgL86+PWib^S4q1$-EsdRP`KbJADj2$| zC04K1UtXX2PsOrH_`ZQRohdd zo!Vv!y9_Ih>S}5&I+c^18t=fWCT!7FUtd<0h6soRf6!_1AT)mZL&*EZW-@j)FYERG zTF`Xu8=FijThGD$TfJGwk_jHCjZ9S^G%8tFHZvXX!Bmb_=E?U3>j%qAPWMNcm8Nq7 z0h{{aIWuYjQt}S{4gcKjh%u2im-C9Uin`_U)(O1K$oKETIpg!U(MCd_c>%XgKfO?R zI$tei_45o?Fk0SpN(#=~_zW?M8PR84B*kgPj2c2-*RI>Y!c8mJLjk@KQBj{$W!_n( zqO7W_vgBxu7bbAA-FYQBJXs*Z?I-Xl->p-gu>bv-gJBnnb!(y!)Y**LJs zhpsQ|>Onz4FnTgH)>ck@Z!=}?k3k7&S^Dp6m{y2#xN%Q76;3Zc-SZ~%UVxU7 zA)dUnG;P`d@SD#2xpBFot(|tPUZr`zhBAtem!V_V0|Ss#QK_k^tyujLCw4Ec?A(e* zd1d%Qe>2_1mw-xq;Ls-U^w7oYdJY3ws;`P6>L(Er{_?I33h(4BIQOzCNS~XH_Kw-{ z!=$8CW%CmJ0pV^Vj-vY$$NAMIZ*ZX^E#Jk|C(Yn%e((cfUJr@%Oku?emiL{hsA)qb zt1u13M=U&#y$sD+rgi|JCgm1)$nv%Lv2bGF*5QwurHv{eC@K~xizi@q@YnmYh6RQH0`B?u|5nQh31AEcUpx>hY3J34Cjncj zV7g^cG13S)!2x`!9ndfUGlX?@rdw2<#!vcHmA$Br^jhIA>5NyU%ZwRMEE~YxikzRoaG8 zrV0)YZb*;T*59c1KA0fny-Q)(A2rwg&Fy*Az*(U-DnR5e5xbYWjV4v_(2Ns5{1v3A z+E80-xhMY}F+7~X?D|yNoRX4KgMOb;jSNk=S*LP4E^hui=iHxpbYa-Lb566lp{Xg< z_+j$E?1X(HzEVpMt@DH=+00D2f6gR_Uy+2FueVtxme*uA;Agzgk-*@e;3kz}v?fZ3&#WnTBE`Vx-xhUR=3#ofL@xBnB+ zZ6r2RaDmQRz3Lfjf#HzE@@2OZgtL%)mQyBDAVAiW%PSG{=;X4To4_Go1~Y+c+#b$F zDyd}9Ks=xkjOipCLN5(`1?4oYprX ze;(EZ>zZD=63)$*zV(>beQpM39){0y;vf%ZT<S-Jv#!hK2KS*~># z1gSw>w_#v7T-+l%JeYz$SXo#3`hM`0iMh=1cQZ?CrS($dY&La7jriN0-8&qXOMd!i zQ#ty!8~_liK&(#3$If1V{~&e0o%oqaUGf7XtmU;8@DHkJ{U>Fox7G~ z_+PnX$nnq+LlG*a5+Y~*LiXQhq)Rup#qE}z27pfx|JiH$3eTW_^%|;h%Kw;?{|^lK zWccUxNdmbt<#ZP=>v(NJq^B-z^rPl)Q$*2&=l44lT(+pfRvasn4oodNjIy&AXc_W* zN_KY41zkh1T_4$efQKgyd`6pW&&d%2{lLC#CzXs_vk_>j{efz9Fp+6I z5Ygjix302ZT2_XSho_{ZG~sY5M8oIxU?(j6dr~R!t2y;)NhuyKu6t7SDEH*Zh?Km% zdBm0~m%vED)s;i?XktRbO?a6}S!t{9+v`?u@Xnf>&--CNIDiuT_U(}Aujj5yTXKq89 zBu!|!1i1x5>gEdbL%Mnw-~Am)pg(-p7$+YS8~?elq|`kvrME^+{lhyb0*z+m2PfXK zpbdXA~ugISZBtyEl$}r^HG6GxCxteNhG?FhI(u z7Dw^?Rnhg)IaYOyljx5lZO{S}PO#vmXxeZ31cX>!0~rL;Z&F7qFN#ufxHaAiK%;7CZ3%Mcg${ZxQ7U{!>OK)on6GAF`Mjkp8|v4vj9 zy24J%TSEN-AYnno9V!}Wz?{mms`%$Q!!8dLq=FjL+^45Swk48Gl^!x^f)az^Q7Dlx zlBPIi$Rm5SLWMa()yU^?>*XA!%41p6l-0&VvT+K0V}(78jniq)-HX(Ke90rORjf=T z+5maZlD4bVLf3ig?v!Fn3>#m^4an&o;lnpQi&MB zy7KAJY*|JwxQJ?X-klqFg~NKa(bLyPksakZp$s33?|Kr&rS|VZZrqsf>)vZczgt~q zB1UO|yzPc-35()6*27k|i`2=(x^N}Sbg33B1v`~z8QasA0`9@KhRP7_POuct0iOfi z!T~XNz2j)%IPV=1WARkFZ$`t@nLtOq-1*%~TUp)fE!g+;dP~>NW|-b^l0n%On+*mE z*o6GTT7RAVZKRT?Otoa5YOZWuKn0r0;3 z-h(0)1V8|3cJ@z!tHdnVb4u%C`jO$q2K$|TD9vEH<91nrTN6I}w>)0N6OYZ7>gQCr z4XvEUiszc2zfUQ-#iOuPcwY|gRr&E>V6~A?*cnNn1A*|^2#KUSjA~mKi%6eryYgg} zoo+5Wh(N!uf`zgj#c16VkUrUq0wq&0^Fw`7F{R?{=b-V`*M32O%I`y3Gdho-g zoWWBckMfNc%(?eIF|}@`g~N@IG#ltFwCPVFKtwo3QQT>gevAo0oiYO$3$iyQQ0CO! z`8IqK;O$S<3<=_XTU1@dTeH-p8<@nkv&!2ZDHTCO?u5e*xSssniKHOzc(D;n#QEZp zGM-qoTbH5tY5(Q!$(|A*b*emClP4Co8(<%}6ypju!MJw)Q>url#Z&pr^L24J@l%4( zrAiXX1;HMqC0@UL@D2OYn4lk8Hnd=OEtH38UbB0g5sSea1dnpNPt7OwWO)^tW0~d` z!Y|qPNj1*Fi!d+pb%EvUk4M+U=he4FJj}zLp7RUh?p19>&3XFO5PshyZY5-;)wu;K ze~6E3KR&mK&w>XObjxVQU1_G{ZJ%B;e4;8hEZ-KY2xU8b{$?wT!4ekRv-A0!nSCbt zWy4+W+ajF|AG04-*oPX2CWlUze?h{G8(JumDV?Bl-ENtr{fUi`8X~dh>O)0O??IFj zNYI`E4v2oomoTTay=;Tm#BhOf)edcxn_$6XU zthdHf%MSTw5d_)8oio_Uk~T?&@u`1LdV(w+-oIi_c~D!QCti44n1vwwPmiG0#9scH zBRRJ}vNTV_N1@A{zN*e(T(+YN9Is}K};F|u%q-|9@3@n zA|;9A(LS6VWFm~8AE>Z2hIM5)I+W|6Wgsq>>L_{h8!<~Nfv=EW+M`r17JQU69h-`P zLqa)5A~KYd`M)f(K(`TjliX(=7PO=@HO^fJS@b}FqM9x%NOO2q5ux#CHtSb&1$(rY zi;RFjinAisGRPQ5Maf7vGYs;bb>>%uDC2}ghyXJuVy9Jp z7Bis487UUgzFY@OCq`|LZ{dZOH7z1DVI_Z^=M?3B0x==~QM*g)Vq3rpHVYN=X%<(AXeY=*cdwCp+<;D=f0#hKV{<5_Hbzk*4RVZ6CqWT^vHguRmOUr6blD~ z21Fhxu@IHC(;Kq)u(zm0(M7q?+{f8=<1L!uNUFS;H2BP;!>yGAJ#y@RE@62(07$RJ zcUZ@#JkC%sObf-rzjv4Z*9*X8G$)GQ>E+}aROA{`F|PZ!La*VbEg}z-mSyk`->1I- zJ-`Gq_cWZdOE$%)zqwul@EbW}@^!xXnO^y!30JT}=dQmgRdjyL>O2dlXovN|DwY~I zj{4EL*qKFG5M-ocAdzdrvS}WWrowS?=AKcXCP)RKi4124&Zg2%@hZiExE023_W8f1 z{_Zgm_qokarDF45;|DQ*{@Q}z@a8tBYmI4>9Q=%458;|{S?_j((54Gt^q##k?sC+js$ zoWOOqx!f41ofX~uX$aK8(qW^-wdxhm8UFlEg)@+UwXjg^Z1eF@P{Um!+HQ*G2#w!) zDpe4aV!C}Bj@}soNsxuCf^670OXq$A)y5i814Z>A)cR5J>89nQ{9DXkDo3&I$z;^t zh83^I+F?^rpKOZv?GP6$;P$yw%Z}u(=VTB@&+#IC!9`Th_4)w>D2(2}x?V2VdStQt zObJ3!6MDOYn31|c03c~>u3IbM*qhd1?5S&%C|EJ3*Y2e1<4%YZ5Fae}%XQ13q10d+ zTR&Ni`wKq5_nFcR$rb->6cZC{O=hWrWZMsehHyXk~2$pfp8UL11tj0t)?UsKZ< z&9Um{gEg}IYHt30QUfEsemM<51tN@Fs5Z}GB| zFa47`0KJXpYw7}L+lQa(2uKf^;>au+Q{Cb^N7#pC1fh`(L zOiC=~pYXcPYJaheP2hNIt`Zh9~ zg#*@g1E1Su#hE=gYz=BQ{Bpdb%_?vXqyAC;`Sq5u!s5@5R2@z&+|-#Kht0iPFI)a_ zq@3v^BCfB?YSB}F809N+tMYd(iS_T_1f8F%V9zH5?|LeT7l<*OJ?`OgC%Qunk@8SF|=f%_GQnsKvjtiCd<*Zd8_*SkKLhwe^ zk!ksLps|07WYFn3ip0n6C+ zaIPjh{{xK~cQY;a>ToW~acRrJ{vP|)ja4$7d=`$H6?Sw>2pUkzE6}ElZTeFz7j5t% z>F=y^?l&Wi`{fQ3vLpYj!xH_5H$OHzgZvhnvb2EhIFH`xv11DsLKj}e@nro|naq`@ zlBH-RC^@b}8W>ftBfYhwkAZ@p&^OK(j6{Z-*^VAc1t<(+x+i$_Byt)Ry*i}J>=7Y1 zjf`{~iHriIgQ*|j-u?kqD*gkO|g`Fflu z($7x2@LkhRz~!&rc3lDzGR8$5j~qP6FVx~{C_#?+SD>FN9h|}6ItMIM9gSIY1Ryf?G{J%TPYYTH2Fcll>fo_%wYHC1(8n!||#t zW)cZHcV~P&8qR$B-=RF1d7WH^WfBKk4Hae^Q1nDdAM?=weB3OZRu#mWqM>v%Y6KLi z0ZSaSHYb9ZuFBw&RL0gymNG zyaYMAQ(rIP*p{*`I%pI@H~UHOW=@Ioq}q3&!F>8Lrt~_b{+gFaZzl;yCG}!NJ|Glp zpbt@H?|f31C?fs=ajCx2DXJ50Q(W|QAaq}fSwqB1X5^9klzjx&+ZfnFi8J9o9`n1|!CAMeL!-)$f z{bN8+RS+Wx*iEP*bf@nV*5def?!w`x0Y_81vl7KjDwuIsOg2~zZy&6FX49a+N%wHR z5{^92t(`1=S!0+uZ|Y;~2$h)T*i6pfkPIzIAth*jwnA?-Yb50u)mZKu?F#0eS)cCC z^nn0`1@klS!p|O#G|n(=t1g)|j(pvN-URIj&ms*ucofMK+U9jyEx8R;xNe=o+ z<%UQ^+)zW|3w2+x-wOi^BQ9A`-WmcdAo!uAhUV|Fu4%&{tq(iQ_YH5`yvaq{@R*q9 zulfTyNs2<9t24(2;`vJ;5;~RHXz&N5nw-0VC!=H++B4}$iZW!ZTj>*9)*n@7gUAOV z>1L6V(r1|&83cqz{!s&TyA`i$#E!!^u~5pMMK@BXdoeF|$iV@1)5V*Y;ohdJUw@$D z#D3iomS>3J(%tq=E@oQ$=Aa=F6B;2K|5*wbRD2(ri$}D__41Qh57bpR1j|M+`L zi+oCGS)^yD?&_lN+=T)!sA4ac_CA%5^V!yxX~TCXy>mM zpT@n}#5*HS({IaMbsgZlVcy}h)!@xb$090wG%e*T;o(3HG?yVF zhKCaE2+87XYOlbkk?7 z8nTMuQXK-L4a&)f%c`sWDqNAjI^48(j1K;FM`)V)`E9uftG)j7Llo&WmI9yVN%B7I zZ6_s7r?aYn!|$=!(bwDC!nI^1`PkHx?zrfN;ktok_>gR___)LlYr7fhyya>OFht!~ zL69Y(!*t-A&-2kwPqPB))vN{=`+~5@5rMAXmJ{62%M%_!vB>zmfZ)M9OsF8SArt8r zp(N~r(5xsKvtp=C5?Qh+Kdfn2NnCR~IwCqDL;9hX;#4R~O7~X zwQ{E5ypqV{DV~Ug#y2}7oyTZg@qqz2-0Y{# znXC~)lRw4^DqEqcd8i_91Hqe z$v|5i4>#Fzb~j9;i_tAS+qNvCesBADjC6lPH4EV--s%0c=d5(R`O#ua6rYn}WR|hz z8ijS1XG1aQL}%V-={^o9gOo=lrk1yuQKSG(u30Uu{d^;#7q#*oGI>oY(0g}P7 zx0BwiuG@WOIw#HB6|lbD+cP3LK$4^W=JJa8_Rde57>uKEqn89s;JZy(>?Wd3N@eEkyJgd1ytup?`R%= z-|L`*q;!hyTRP>+r{2At_y0mbyYjBR?>LUx@~&5J;k{)TgEDWVBKhTY`pBCQ$PSF` zGIaR^X5zP13Z!v4t$ro-gw$pZMDf00`gNe^^}V;r4DHK9D~;m$F2VhcNon!w^;I1h z2GvpGIRV%8)!2zR{sUlujLY?Q++*ARv`YJlWgzO8(`9q{If_bevz_0Zw9O6Q+cfJ^ z=>7Ve!tX#{TWYtbdF-q_$he{nC(Sp8GKVr7yi+dmG6$kb8m8QOpO+_GRfRO|vU8)hzSowZrK1}i9R=SUeQI61>V~0z(9?Iw zF8MvmwfmIq%KSL0!owUZ_7klRQilzbD`5r-4*3Hik4nRc)FAv)hBoujkg7uq{zb{WE*qmdYTaWOh-P{6=j|r$cASs$_ zE^L1LrrSdK%9Fu%qHzO`20HcmpQlv6)~~vin|QLH&bs*baDCTalA{rxl`ji9iS?-U zJiwKpZ}Gy%(eX8Wf_Ur0lm5q&!Wo2bmR8&yDz?isg8e5Nk zBvmE>TK%k;BBIEeBH{)LW0~P<@P1qtdWN#NY`%sm% ze=A`&+S27^p{c3)z632Tyo0XlB+RZ2-}jqH##3gFg=f7^_}0gvLni#TrNkF2l3=iI z`tf9%O-)z%@$Iz-`;fEQ!rwv2W6+dx=i(T_*Ng@Z$n1q;MS^N=5^XA@y$*VFcRWUC zl0}elE*baP-mop2Y&i+LB&;vDI61UBayG~o<6b6s8CjZSa*{mk#W+qU{(r#E=AV*Nb|=kBy7985qD z0!6P_hyAbB+0gj24RMp!xUM?74pG&(gU&VSbu)Xwywfvhh-bS`_>xyEl88^Veq&1h zj{JN94F|e)6b_?oZCWdKE;7*<9AaWy=8T@fr=>&Ap~}r4rP4agd}kc5P>Jn-`g;SY zfj+JfGKd|8V+_7|BHP9#IfDEMGv)V|_qnYT%vtVM9#yzVVhr+S^vpN{E$j)jAT~r$ z)!5W(!FKSn&ntVZwL>j+Wx|gg!>JdpBl)rBhHqLX~qFSpZ zg8dmA>wQS^$s!@M_^BkzoXQ!L*%K>;WtB4>Wz2L@s-QC}Vr@-1x86PqS=%*}lc(wLd|+3zlCOfPf&8%y;fy(eAd)fCOOgb7Sw~U&P@3 z{?swEXWk4or8Ri<3-=n6AFJ>|W>-2LP!SWgUR81SL-=j3Bzf1{`_=?Y(|lrzF4i#o z^$M0`d3w5qix-O^UMq4(=BtrU@tOa_iA`Kgz)Oba;(M1hmawtP#)AeluV2j5*@~@Q zT8KXQ9p!{?Z8T4qvSBaZ|!QY@~`AO+(MgA1Qk05d~0xmgQ!Mz^J?Mv zqAC^2MK_)kH*Lvxc`zr$3;@GMXAcBNM{rO8iCB2aj&ef-PlieL3z^tlW~9~TRX{HQ z7;w7oxDV$PYX!nV0MM!M1>Ib1Pte$q=JU=?*`(F18*%4bYv*u#kXM#p+wjnG4A8or zxD+T{66$!>s#j)@yp=6va4`9nFOOAoS{0o%q#eOI>z_O<{~Qg^aXHT)In^{9Ab$Jh zpNpn{eRsL4>{+j~uLA(=TD5;(xlYpDl?j%kR5v=Vx@`IGPS=)y;Do1od7>m<0`_IslB3i}|x1$?@@H zQ;Kej!1MhqRU}LFq$J-?1-BsQ-jL89AL^1RJT3l8ASa|C2nTfd;Z8NIqTsY?bg?7g z&>G#s3G}~8d+VUMnxJoV6EqMY5C{&z-Q8Um2_Br_zPMYEz@ouDxVt+94esvlZi{m_ z&-1)>Z{505^?mct);VWqPM@Bho}QlW-#B&HYXV9~CXZG`0U7gBq3`km>vO%4Ii-^{ z#_uR0@nWf4oz3*Sw_#-Q%yq=`!;N4Z-jzG5k01O$e9);)p?asM#(pRMxxz)#==+i$ z&bE<>;t^N0=jJ;_>bn)6D2c7KIl{#RvY=t0Y(7Bl3X_NijXg3E;m)1uq`m zM%Vtey6mNz=Izt3n1{p4H{M#o6X%s*0+cFf%D(qAE1D>{$E`k{7;)T7(c=MxZ$^Wn zd{tde7_J_qI?K-leUIYDUT)9pM8qqYmfB8$&WCU+l9WMGM^(7U>T$Xb8t(y9s1^F! zt_wgjPMk)s9*xd5O{QNfe}3TqCJz8G%Mwlt#e`RSq0NoXi`zvJ7n)zyQUWmEgu!wU zef3nOAD9Kb{@~~Rt|zEtxHvHT6R(Z5Lp`nXCvg#?=fB%wkz)aFg#gr+vqst!a_GI>CO2fcit62Q`b5aF~)t}3OOfb#m$0I6E;x1EsXTgp10tS9`VLw(vFrw0oIdP@ff-WZ4yFBp;o)-{4knliWE`g>$^h}TcXGYLGw zAavGBq7id%HHgJ5I>#q6+(Uh$+Qexr^lt#0pq4hbGb2#|bGc>nz;gX~G!|J_@$9|0 zuJ3`_nu(6r&GS?+bl2nfX{bT)R>D5Hz?a_K*8MQRbwq*ltzU;A@2>aott2&DXpf#do(D|V2!>0F180tR(0AX)0 zMC`5r9y8`NJ(MR4OR?Brq7e`HSE*Tfro81AT4pf`e!lTjrYME<6xe1EE#6#kvvEcx zk&}?vOFGJX(*%DRT+wzt0pj{*M5{Q69M-r8JQ!EmQ z>EU`L#%4qx0DW1dFVilX7e!G0YtCU(y9nn@G@-L85>keM#6L2y$3DhYII_^VexjH_ zDKShK>vW98(c46NH+#OT8Q){B{8oj+PnmR4(QWTNEJb=f;}x%21bn#w-Rc<0Tfae{ zWb>JJNx%m=)M^g-J2qZXgj{lh`G$Bjq7HYp(H>+bfvJ<3EsrodDm;h5UX9d+)121H zxn&J^9nr7)2%8MC<>^JC4gvV49iqXQqGY9F+NW z!`i-rX`Nv)kzTegzXyvry#Mqg2}MCGoDgqW_}z4)cyQ_^x3}Sp;B&x;Wf}7ObgzyR z%9!z1WQRI!X4JQzmu1OYu{{08aVD;bl0d(EtFj0g5*Nn|5)pPOIyuBFJhJ0(pu2De z1lPZg9Bj@-ML2*_@yDi{Y7$aaZ~PgyrE%G)b2aXgn=SH(n~WYR%>h5ji*%iDdx`?# zi(6v==S`Nk1gLErxA}0=O>1guXD;I6$E6hvfE(Ak8VN&q25H3pF6=5=;wcAl+V7#U`k-UQ*cgSW>l~hZk?tRr7n~7 zQF-faXfBGw(x2-ri;KgQCyp2^YRAViO*nuquYD#18*1quAQkxaUD}{3HJBiECthRV zkl+6;Gpe^yq0%Y%_NV$4is++UtHbqP5~mTigZs4gynGa7M>anD?mJ*^Dg49H#%`-RoV7|oi61-)D;z(+ z-!KtruT*r>2T=gGP>|Y6sU$0}#EW%->2bAOPT*JhuDN+r+pNBC2K0onp+lrboZJ=x zo4L-Kyz!ZZM5=^xHVA3kYM0R96nuvyQ9cxSfZaFCsYnZ6?A|(gF)0i&X@~|ky69aR z2TBSC-s!%_Y{TrAmywEK`TvOp*zg49>Rycv*cUjarJZ4yTOZ$~S`|1qf2gE-jK@|V zZM@?0?kelJ#s{)jyov$xZ}ZL+6CQegCNx`o#!7Q3h|++Iq729Q8WCteuiqoMmT#hV z?yGAd3(j@6b$QyJ4*XKgR<%x5)<;oJRHieoU!5GH_>rjNCrwqdOq84~rK)8j6bFl3 z2K{XcWYlVkM~vE>*Y7Rh%NhODbf-ubhOo`7gU#k}!b$TAT<|YXs|huTfIz;+6d_*9 zGLy2V=Apk^6OA%~{t*R$VFkL`%bJ0QkA92X8lCb=Ei0VQOq<*)AaO`6rMcvJ5Qnp! z52wugq>)+`@a||f(r5nW8cSBu7g7`Hao(SNK#uhb9@`#vqp&aom=pky3m#%Z{5}cq zQ|lig9sTArk7%*|0TmlBlVBWtLbv64XqnVkgI>Fc?n2`QYRHrPmWFel?gk-VY-S6R zZUoHl)!mo8+s*;|Gqclj=ctfcP82s9YM%9-wpf*LKqd}mR$bA~TR&@#WUOKwu}4T0 z=$`k3XpjB9kn#5XHrm>qkETE%1RTneo_^E|SIEmv-xDqUaFY!g4#Yy3*I4?h#K4Z? zAgCRH`!;nM{cWJ%2x5a=OgTOkZhZb&^wpGm|0n^iM$`SRPJWHT(ig<<2FMf66Z;4g zt$)1Ms2|qpbDphrVBZYutE>Pxr9yh=(*;iG9DF^_tX{p*^ekb{d8{b33;k_I%-Hb5 zfjM-jP{&_-s#!wyhKzhXRTObO(d^n<=8oFc$8A+4Y?K(7x`r67Xnlsa4MrU}McPLP zNYp%)mjMvL5)_#Tj{W;QwH&6$ku+Sf0AA0g{JA*Ho;$@F>5E1y+C$N=*eBe>D$1${ zQ@#t@nn-VEXurz6uC00pOR4R6176cT&#q;X$zxafz6a|0JHcW}9P7@tA07=Bw;VQ? zpO$-Ld5TCl&0t}*gdmYpOP^(t|NQx7$}@kZ36chh!W6}l{{7QSC@Kmqd0CDUvdZJw zUN9NQZXx#x7!jXMI9#Drz^?VkL0i1-S<*BZM9N|Yuvug`$j zJ2CMHkTOprmsBU~4U)BpzRVtZscUh{nsU6FuIrs_dX9%jyJsf>V&Z+ylahTyWs&b- zZcGZ&X)}pn%fxn!_wFbq|NV2-!6=mwzr(FWaU5<#(&jM~?$L{8?mFVLr53;0pY5dz zb5DQ2xakYb5l?U9y77p%yetcP4)suElNZ#p{6x&YONert zR9bk-NAv?7JCOl}9dV%syw#?o#dziFvbvoBPkdHJ-^+TXbheqvrLXF$%Rhg{+EQT% z&QXo&*44aPMd^G9j_O{M_zD2pSJ$-!4fj_}bb0;yMDdn^o;1Xyx8J?xI)`e{JUEV! zNhUln^M=Ahi2`&AM60Q}E*x!Yqkt({t*dEN+Vbb$aevibaO#UFLb#p(qwnT(J(cki z=X8@~P@|dxxi5bBeT9QA(>5mPIzDRw^5s0sM#oi$u_~p0+Kg>g;(FoGBP9(TJ*;!w z=<*L|`Hr-f3K>a&*XBou>r){51Q~?rlS}E~5L-ITVZNR(R6D+aEQhlgOzAJ4l=}8Q zS)X}N++=n9IX5JcPB6a&xGVcM70OvJ4eZ(O5+@>FhQ+3TP)(QRXJ`D8J{M*}A(LDc zvmi`(_GA`bj%SOLMxLk|nh(v;i@a;_RNJ^=11nim56S#A5>xOQS=6XGZh>wotl=fc z2+w#zLJF-wjXvgDN#d<8kVxeQ77ntP`MI0v);dq1fFP;oR*I1R#im+?0)G6qIN0g1 zTEsBv_`Ly9#<^8(>zA{SbhucO;+}_hzCPzgdb}RY=I2upW;oR!O-LtsY?en#QUXOt zV?{5r2rYqEEmyCPgJxhAeK+Qz!KQPyGq?Mlxpw8Z<+vFRxm=Ot>2`7^Jw8{#$>iy~ zAE{wAt*>a=KCywT?sU4-%#$Nu8q++>fX*nKHG7BV8D-7(+)HX zGbtRi6O7q|kziMT%$Kdws5Q3*s-dnkv;l&f^9)J^3?=DX@3}p}`_u6FHSKWpg`Aw7 z3@<$NcUfw|5iPu4-9Sa}LK)2QqqIlHW3qJ91`N2R`7mtRk2NL|7Py8JG?18u{s*c- zV8g);ku94{`uzAG9E%jYB16@Dj2-R`E;bE0l5UXObu&HC3JIf!*JfW?Y-Sdsr|r9R z?Zk)9wou`H7pCCl!2Krnfeskv%?U%6ZDaBfm6|&MgBc(P{uZM|auwaMB7d*G=i7-*E z?O_qf)GsPa=;+gtbiz<4tFlbt>*MzF=S*E zEsuqJ?{;&PEGNn0W?A!{RK+&?TIKsFj?GE|XpxM|l`!`Q&^3aqonQZ^FnEn$6;T;Z zmrcCMUT)|I-p-BTJ*RxMG-4>KDb#qSeRNaP%j$0~YDf6)Q7@M*BH9`MNZB8}~qd@nGji!~>93E!{LnzU_kQ^ncWic(ye9p~jqQ@84v{lvi zw#vrbT1sYC3Nki^X;Soq7OwKEUaxabZTy#y{6Q3CJX7B9D)_9MCt71dNkv9Rm za8=U5?Bq)C6_g~el|{9h%LXh-OAgjO$tGx4SUR~$hZ8^v3oqY-tJox^tXph!W!YB(#t`crUwnvOT4Q;n=|vr%uu z=aNX*X@5XtVUNBW(|Q9((GoaNXL8R=Q==i8sT0$ohkTfdZbeSo(PC<*B+@x5t8MHc zX=tdLlpZpj;_XgR$8r&^7K9Hhe!jrPwUBxDDLJHn?Q2bqnawLq_1 z5;%+!p9<{pW@vC#|6s^yf81z$ExdnRD-WxQF-B>!KHS1?O6@n)z?s-FtHpA|H7fUX z8J@9Cw6xJJ6N$+_^Eo>S0D@C=+p|8_$m{MX4`KwtS5h&lsh*I(Bz(qCb;%Q%;%ydt znn>ko*5RYjprdou%=tmVtu@%YKY^Xm@X~FlQPz@axAeKvAo$*l`K4p`c%n3|TB zaaGeMA_SdD8(N*XL z?&BK7$Ch`$cLuxtr7J9hX$cDttJ?G8ag6^}0tG$V?4$rB?|aeIum>u%xO7^eU6H}V z+EJGg*iJ@aZLtirxD*!CX6Duks1g-Zj_0FFPJeU!Gc?`qUf~e@jWcyenrWii*H?;W zYdCC-Fu^0&HNV%AZ#hZ2*CjVSilWzHvAn(FSDL3xOb9YkZ6zD)Hh`_fDqCT}th+=x zJ!)JAeNnO0=`P>Ui_~b~s;W+ZV z-sHMxi=r4Zi`4G1dBMfS<>3F3mF3R|ThPMr;2w63#{FJ7#Z3(09cK{>exF=Q>e`tbZ#O1@ZG)cX|Yanblff5V3E^a#{5FO;85^>)>P9*DvPX z`JmN|w&J{;k(S5U2D7tLM|AWV?2emDATh(BYmdY<*0ArzfyI!l=Vn)Ffw{7Kwo1yl z_KQ!fZH$E{+^b>yYv=}iDtG|n1{}NA(pz2oyQ5xsO`A%`=UZIzlw|V7Ws-tF|{09^TAfxADw1gFv9=kAU-*JGg+uh_bky{NORt&0Ub{q>*4Y7W(X-EVOD_>)r zq46OekX{-o!2=o_OF!ATifMV0_F|`*E#tj?Ep~tw_ zsngk)Ai;nNk2L=5Yi-VNK2mUNua){8qucX?$fxYIH*bjfJBr4qT7;TW6$07(bUBC5 z`$W~|z$};d2S?6}zbsiyGD%32WWvLd2p3BZF2HKN559?()$<)D@oP+idZ%OIan)Oj zD>&e7$JeCC5w5jXp_M%RZX?H+v(PK3EI4jO8d02qnR4xI7a*nFY<9u zwE=K=ef8<}j}`kQcpiO8of~^5B70U$?t{@b)q`8m?eq?%**ZA>r^6ZBm{7~Z)>y&w z&DGsY>?qzG9|Pr50uIZN^uk|B^uWikZ++xobh5h30+3IFtK6BEDWVr!Tg4dRLH{If~#B=q|#!rpzu>y z$mkJ>y5gg@Y}2s$J*w`^{-Tm(3pk|TF!RFu{ni5xKdnXcPn_fk1Sa9@8_QR;RQ8&# z5NduuUG{#1BR4KmBN-UV)o9+v>}=zmR#3*>Q}^_DGd=s~psM_8eT|R_NNpMTlfy#5 z>*O$e8#r)n&svvm#IX9E@-wZ{$YH$Yb?#*yu*5`U_)LWqkIUK$BfHg)B3#xIm#aJw zA^G~y$b0W(seS~Lpu^-|Ssok19SRL%>57gbO~qJ{|Koc2vftG3n~Q}8irFK+#(-(= z?=2kzy=a=gP9!>5tGV|^9983hylv!rk?(awjHc@u>~zC1{ulD2q4U{5G0(v)R+pi4FarIkTJoLAk@t zME5kXw*Gc`yJobCzp?SIenUZ0E`jjPNGcz7Bw#_i;9dG~Utu8mnd|FwqlH0+V4G)# z{m9B&d}3$e4%qvArM?Cq^`%*Uy*8D#R>iT&25#8g@yj0gx z9!CT#X=0QHM@-+r1e14^p5bVSpach=DI%fg#0{AjQ3Q}O>;Us=wlduD%y->7Y$a4= zvtaeoW>JccqsZoBxjX6|HVVIaI1dA5*c~w)#ofkwLq{Rg67mya4Xbdd1S>jcK1}W-@+JX z_lUCVBL6s>`Gr&&U;8i`VHbJxmj}sxeYqAz1L`-|Tyd6;an&e8o29lxZb44C=s${< z_p^Dxodi{UOd?w=9{KD7pSQqp4+$WV+H_+Gawm@Fdu!orueT@9$rBDa8mb)N-K<^p zk2hc_YyqYSw&VCFf^EmwIx+k-`r!2aW%-PZ*|`1qh-`20bo9p-T}eq0k^{5blOlyA4P9Y6|6ayDfSt&YTswmwtpC| z_|6LrHM3q)>BJBHQ&4g9OPEtMN|~6ZgY$j~t7)63^cjm~l2t37<_b2&%6@8tT$d_N zI-DNiKP|ES#3j?np~_rw7ztL-gN+_KdS`dM_H7Bz{N`S5l!MebAU3l$Qt?q;SGgyh z6AAgVglV;SJjy1Rr=7%JxdW4eL%A9!i4@(49QMFpHNr*hq7}o&qU!WAMw5g@dMmqJ zX1dXQk^fcSn3G%PRGNRD1u~LRwgtk=Sim6do=2DS?S9 z+-Ig^rg*p&8MD=Vvye>(HhOhgXBy!19g`CIo4azXX^_1wi^HSAMWg8pM?wLrS?YG} zU`YnSE%aN8r{@>+M<#kV>#5Hv$D4^0#m~l_e+M7KdZ6*s+RW!A&Ro?)8b0LqPjiVJ z{H`~b+ehH~{;n@X+^VV5$0~x;sB&e~w)E;Fhp+VmohxQbz!Zk)ekI=*W#L>grXol! z^vH$dVit*D{HPDIIQRHNvUp`5nVPnn zQ13P*m$pNSL@Iu2sxAwOMb%6SVL?JgTTrb{geT_^Qf?~fGi{)eG*WCCEXeLJwAf_B zF1jHi!uriGSdH^l^@CA3xuo29v98yH;^CU$Z9BW-vt9B-ujT8Xp_v0@FEsB%?^C`t zMYv{-yxqJwu@m}Hk|u9suILr&7f6W^i0e!m-$OOU@kENatnfN!Jx%LS&mMVeiC}tN zQ9{TUUy1;^QQ?IBIMRSTva(aimHNp5Xd1VpaBNu3>}gx*SYe=A{M#G)P8taw9Dk80 zL)Cddah~bXMPKqpZ$sxQ^)24^x4k)`C@t4*b*;}R#_Z$$?|T;p#HPPi@_Y%IEJhmT znL8|Yo#iB`yGwvak!7M=uC3^zX#8CK?C=0YMuxm2h2p`g&7d8IR)!0(S)H_+@#^s%mq{N;SOwk>{$v@-TYvC>v*c+*k(bo;a)`3@|5m# zOR|IE&btNKpYh(!%db{hwm%1QLS21t1YD|mJ+i^03GNdaAa>vLy9^;TnMh|G2&S#; zH6P^AQ3?l~{pZDg@N_PJwZ>Msn8SA^Qbc2wg|chCVl#px&jz?-Ctp@%vnei~@UCH5 zd)Vv zh({OirwJjZC~|YMTxxI;?MD2ZUye%epsrdJF_k!7wN?XSrqb!77GNO|-Jk(faI;&HyUlh$XNK3+zx-6z-dtNrz94YK+%Hpco=&1R)czVT(T^BX6rEKt#PTdi%Z{D(ds@tgtpei!Y5U+Xcid()#7%f z7#_M3>3wkMz*C{qn9EBpqI)Y#b>;5JvV6hViYD{isy$tkU6+<9h?&p*PGZd{@6miz z$Bi!9K@Sc^Fwhsr!dlNG1bI=Z)0biV66mfL%k9LXD?$#tARmWEf}*J5u(`dv3KNyl zu63dxSMg4SHwU|%bxEG^0q2vB8=jTrGATLvu%Yn@0=%8Ru4Ww*{h)>M2#i1Mo`2m_ zDirW1r2b?u@Pm(rvjhxs1X5M4=ckT>tX4f|-gL?22EH^W5R`l-@?{HSIYcPH)6i7| zAxU7lM=`C{dy!FtmZE#5P9By%Ugw}gK-aDRSre_P!H zPdUg!^5^=+;l5|`L@U5{#Uzi zbL+&dH0Yyuiwa9w;DZn}-=D~GDY{$e3cCfWMWfO?Nf(}07vpK$8T(0?mQk#{8JmBh zBXK${qc2fA&iW?OH+nKp&%jVtUakNHZauMEpEDDc>FQDzRP6Y4DyJJBaE0(K0HE>$VBy1{4b%O=x6R+%TMjoqc+C z*3{T(SGMB*@$Ndq%XSS^`eFa_GSp+T_svbSl_LsQIETyj^JgbtXSEJyty8KNhmbf&9ugJiGtbP8#N{ookrS0-Oi0~ln1|yHLPYL1IN`sY9{FvJ-7x+ zU+0e4=3Ctzt*uKhj3b?Ru_B6Ixn}RgenG=NWngYAWN2ArBYwtoN8~xT)~X+uPtxH_ z#741a9II#n0J!^q&j?62mQ?>3G_NUFNU-%Yo3QlRSS9THD1PR=oT}<50_6Z95lUB5 zPpQba*-Wtxk!l|9UB5T42wplhFh~30EI9)7vp(lFIHe+0v$I0Y&#DrRcBeN{ZrF?_8KZ=9=W_ED>R9TJGAoyED9wuf>c>F0;TcBz z$dbcByXYZ}>mImgCYQhv&GKm(NLAw@C5~xqBBIWlaE*mc`+TFbRU%YaBI`t|?0iT} zrOS>;STJtE3e9uIq>m2#zG;`aZD(PeQfm;VAl{rA_<{5DyYvIQ zz=yWvot^>zqj>pgDg{}@*ho;tdp`W1)D<)n9O(~KoFH4kIbRrZ{EJil1sF8VLE+(& zcHMw1r_^aq{CzYIi>yGhzm9Q3y*JvG-Dsrb%{>PR3ldt)TL!tl^bqTD7uTf7=1M{` z-qj_E1jHEuZhG)UoJe0Fv$tzJk+BI(Z2T8KOKZDrHv>bS3+Tc}99D*Ee0IIEN(3Ck zh}eMZM94xi8K5GE%xU@2!59{X8#f$D0`_@VJ&GDD8U@e{ANC#U_!Cp!CS!;PjQ?60 zJ|>)BA)WuxjP2Cs_F=f3=$kMb1K=|hj+{dgTZ9(iNA$iE08jwPqCyX{004xcm}q~C zF%)zSTK-oq{fzR`evjFWf~Du2)7owQSZ?|BWJjCwEy7uJLQXdzZHXR|P2?$Sjg5)T ztX{G%R59cS&E?dC2aoDqW%KEW)+4<`1J4Y$4Uca6>OMY#MskSX!=?pJ*`of$Lh&XE z2)sfoXpF3(aA1syz?gC08QsG&(Vo7(#gki=S5GL2V9e@PC|6wXBAd$ptF-0Dp3)GBP+y)j7E-SfHmQ~SKb5}8 z4!Y%O*Xk!1F3WokLIv(83iK_R!3)hQZXA}{EEz0@{=2rD7@t_-{*fI&B2^0mc25{k zB&?0Gq+cP*Ej#xDu4|*F?lKx*jz7$$!RzB*fZ0OwvYL*Nd#s*^FC66KjKaX zuYRAaX|AtKHe$FrBU3}aqw6!7`e3-#0DP#N=js$~%Kn2RW=qo!faJD9AND^s z*wj@#I7*8C{JiPUEs62Qi7W{O{a9%EhqVBJA2d_@|8{A&7=toQs$|l}ZGJ@qeFhyz ze!qGRJ_kh~{z?A#`frW@=W*qyBH9V9v|kfb8+r~I@6 z&voibOeq~s_x-=kAKtFCi(~tTP%p?p^v%}^3ZQXae|g)?(GoOCSeyIh*f(Tib+&MP zzu+AYYdtTN=28E9s9@+OewgNyx4izC6YxqVlFF^itvWkl0j2EkrwNeYzn6)p|FpTe z4k}6`BBsUt5(?D^)o%d8Xq`MOZ0hAac1*@1xopkGKXVVb#s1~QIGZ`I{BS~(=%$|x z!ZckV+#>TFw86jRcwppwFCR9ZW<<0T>5lzs{X77$ zigqm&7e}Q-k?Zm!{f}+YP6|91$_W8DIae{s3xS&b|23szFXFm8=%e7RLLLx)1*Peq zum5TpzD%vE$zUJ z&Q};yQAC@FnL~OO^(l=_#!^7Rj*;BL7rW~4N=7o4oJBHxL+3v^n#+Ucucr=zqh2VA z>)cQeIgE3+Qd_;_#bN@Mi2G+2gB;seYjc)Cm6W+C{ClN3EppSU4;N;R7{R*Hsp!Ju z?Oj-Bj5Nt!8(0$};-`aW9qBF3W^kJ67DX)0;VgOo_OHOw1BQ1uV|m(ksQuSW_V$M5 za*o#|HS1MNg(m*t@zX3nc6 zA(B?@JzssH`{WKZG>wo+((Y140ako2D9fPS7mlK2sg@pgi+M!JO$fJpdMExZlrbDJp|7NI zr@!+_3}B7pC<4NzHq#*RIm9qql(u+KRdI+mx2vPfTv1N8Rj$XlPa5RjYGNMqIaD@c zxutD>4UeZ%68%3Ja=)F>&{eVm&y5KxOqj$+lnnE^_DR^kV}!u3X~i}T4!~V@(EJMuET5X{Nsln5vz_r$gJ*De zLIxMz2WE+(*z|W&Kq5Pshu_*32P*2>aJpJ^kb_jnl8>c)F;4A^;8X8^y8O!TqjdCp z9^WONIZ-aHFNfwlXlZHb>ZT_o^zpcWPD3%1iG1`+?7#)(Z1eKm#DJCkl?bS+vP)|1 zlwqk>l2Nv7Bru><%UR*@atG__(G3?Aud^?~_*~%(7_t$c+I#vvSN_2--$L)F{@ z)MW6qyqr3utSXEoYFoCxFH3oyw+le&_IpDrGNQ&37yjqDrS`>;yzPJ6UXNQBH=2ycZTM=NVG#aa zu$>3ig-xC)^9d3)i!X(jB$tl=p%~ehUSmXC{bg}sLs<86Hb=1o5{otm)zg0`tXl*a z=tQ;CLWyfcNTr9P_1@`(daJIfPeHf2&|*SkiBeav(dF^SAl^;p*~}>;E$zaI0CQMj z#LQB?4IPNPakQePIMg+c-I77SsQrheQ1sf|_8!* zxogznmDAME1}*2f4q8ni?=q;dzOe0kL7osaPRnEw!L~lj+G@;3i*%lEm5s{E%ivCj z&f3gWEfMYKnk)1(?ybB{4c!hRy&ivBowYh8>5^`Mbe;yT(dHgJsI-8br#SXUFxaT3 z#EPCRl@lki32iT47bcLEXp4hpZ0Q8)gi3Ymxi;SSbMXK4%hEXPO=yDS=8l3pXION^ zNE{yxzT2ngTAnrtbu5cP58MKpPt8Wxv3XF4w6*T_!0PnTi}y1?CZx@3t>slH=F1D{6HnruA4W1C3T-DK6Tp8bU)2p zIGmbNl@+)D=*(QDL9YgMb9(s3=m#G7?Ifkyv8SbdFON|-IK()%SEa6Cc7|`y*|0(^s-*GD0nzC5)`X*tyWQ^?A2Z5`JiB^2#{`Gva$W$KF7didu;t;Edl3E=0qQw_%+7cpd5$79iX|>CH_3p)_vzGv zAPMJl5{ug%oNJo>n+p1Y$Ct1uBDdCycquCF#_eGoZ^)L>^+OvFd+c?0CKwOodzwZc zAeuh_c{Y+W9{)aeCZJocApR5YBc**L&6KVH$-kD+fqub8oIb6NV0AKY!4FlP@{*K& zb4BszNzkPz$o+P8ICC~}hr#W-9aq9r_M>Oe%iS&e>%5SnLId~5M?H^p*DYPgFpFx7 z-evduJ}X;(K4xmu?WWj<(k104Pc)M}w1YWz*VWnRITR}*PBpF8?&u_AQ#nLKj97aF z=W-fTi^Q#eW2qzxJw6yQ zVMpL6XtkIe_@g|$K0p3)YN4T>ZueXX1?|bg{E3XLS(k*)oG!$Xe@~XM{{&dCr3@@A zEDQ+?<4Xvbi$9;?bB>h@4{@Ze*x`BSfRM;?bJuGn^q_%tWK-iw_I5s*CzO=c{*1E z;G^6&wAZq2JRTRl6l$-xXH080$jbmsfd;|ljJ|hLQRj^xbUnNuCMPEk4-ac=*@wCQ zv2^nh0Uqpqg|(6Td{dt8`X)}z)+{V8!^riI&(>Fh&*?MGhZ~gl)9scn!%;*V_SQPk z_3+=>03j9!Zit;rF*UXTH`1L$I>CIa#W#QK%2p@=9%A00U{u?WU=m? z2~^(hCVjcwOZOCx3A}dpZDwN&_kElvjK>!6R8g>Vw7gpfcl0NAxX6QY^x98%<^-4b zzWKk$jeT{y>USfhy*tlXLx_^6(X6g)oXC_eGShK;b{DhaYU4Ud=STl7=zRk|+Z=0p z9R*WbKD^xQVaKyOkZk+a;^(iX5J~5{?{z$G<9U|XxQU23Pn!4=@b29#?7ch}kz|RJ zBVmtvc*@+QLtbe5LV^~MtQGtp_gh;J*N0er zo&x!S+L-YM9|&;LIg#Ja-FRzMU*CIcNj~S{W~lM}dl7g{;IVr?6?BSE49d@j z^F3Le89N$>&nhqk=%|wv!EB_{-(0ZE9()=uCR_4zC2>Q{x8oCS8n>F%@?xnd=4BwT zH!Uw|S;^w!xlsxZKI)AF9JCAjR*O}6^n0h@agw31Nl+2CTTJj^rMVnftt!3xl~GlX zZ55Yvh)o_if*XxaW~1BYxG!$h`rU>!vX9STCYPc3K83F3MbV5-WP@Yudq;v8uW?Ll8ey zui3Xs5QPtQGeB)PfDiTvkBEKvaq5%o#6zg_Zr6HG7?~*8Nm7r-vP*vMdg95$vGAvH z&hyud_z96mk&+%%K1y1TWAQ&`z*{#5^_MhO3);Sq(4oM#56z?Fe(nh;K(91R$wid#5NXyQJa5U1Osu1f-y_x}FYdDDh6~Y^ zYT*;DUglxOxvYJJJb`wpYF=7Y&H5VOXskl>T|$h3c9j;sN+8tGpQ&cdmANIh2d|^c z6+qYUmZQjbVzy5Omp3hrLOx@E@-pwzS_<@VjL9dHCUy~-hfegNQ7s`+mFiR|PB`{u z&{ZBjbg%25xhMa~Lr+i7)|OEL4&8pF07x0&6pmmeYRko?4JjeV-^qEdT&+Eem&(9D zPc)O_Rov89`k%GNtgZH6oqaG4^?Z$&Nr%etMh2igVg5vsI(k1ob6GqY;b{8lj~?p` zm<{D$f0#f*f#vA&SR)&xy##&(>O=YKIS@uOjcR_mEee;hfuhFz+a(^9ltZkeZvnzw zKRD!|mhpeLp+A2u@XR4)%=hkafIERueJB71_litRuEk;u1u*afH~tF1sI Date: Thu, 10 Sep 2020 00:26:48 +0200 Subject: [PATCH 04/17] Add cloud shell txt --- .../cloud-shell-readme.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt b/cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt new file mode 100644 index 000000000..e799e3e99 --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt @@ -0,0 +1,13 @@ + + +################################# Quickstart ################################# + +- terraform init +- terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT \ + -var --organization=ORGANIZATION_ID \ + -var --bq-table=TABLE_NAME \ + -var --bq-project=PROJECT_ID \ + -var --bq-dataset=DATASET_NAME + +Refer to the README.md file for more info. + From e7955fb1ef91a2bd868460743278bda3f1b9ed9b Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 10 Sep 2020 15:18:32 +0200 Subject: [PATCH 05/17] Update main --- .../main.tf | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 06372c04d..5c51c9fd4 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -14,6 +14,10 @@ * limitations under the License. */ +############################################################################### +# Projects # +############################################################################### + module "project" { source = "../../modules/project" name = var.project_id @@ -48,6 +52,10 @@ module "service-account" { } } +############################################################################### +# Pub/Sub # +############################################################################### + module "pubsub" { source = "../../modules/pubsub" project_id = module.project.project_id @@ -59,6 +67,10 @@ module "pubsub" { # at the project level via roles/cloudscheduler.serviceAgent } +############################################################################### +# Cloud Function # +############################################################################### + module "cf" { source = "../../modules/cloud-function" project_id = module.project.project_id @@ -84,6 +96,14 @@ module "cf" { } } +resource "random_pet" "random" { + length = 1 +} + +############################################################################### +# Cloud Scheduler # +############################################################################### + resource "google_app_engine_application" "app" { project = module.project.project_id location_id = "europe-west" @@ -110,6 +130,10 @@ resource "google_cloud_scheduler_job" "job" { } } +############################################################################### +# Bigquery # +############################################################################### + module "bigquery-dataset" { source = "../../modules/bigquery-dataset" project_id = module.project.project_id @@ -121,7 +145,3 @@ module "bigquery-dataset" { owner = module.service-account.email } } - -resource "random_pet" "random" { - length = 1 -} From 3f57764ac958d20df09716753b21bb16d4a86a9c Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 10 Sep 2020 15:26:30 +0200 Subject: [PATCH 06/17] Update main --- .../scheduled-asset-inventory-export-bq/main.tf | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 5c51c9fd4..3fd065024 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -17,18 +17,12 @@ ############################################################################### # Projects # ############################################################################### - module "project" { source = "../../modules/project" name = var.project_id parent = "folders/572946148602" billing_account = "0074F6-6FDF57-1B2DD0" project_create = var.project_create - iam_additive_bindings = { - "user:admin@caggioland.com" = [ - "roles/owner" - ] - } services = [ "bigquery.googleapis.com", "cloudasset.googleapis.com", @@ -55,7 +49,6 @@ module "service-account" { ############################################################################### # Pub/Sub # ############################################################################### - module "pubsub" { source = "../../modules/pubsub" project_id = module.project.project_id @@ -70,7 +63,6 @@ module "pubsub" { ############################################################################### # Cloud Function # ############################################################################### - module "cf" { source = "../../modules/cloud-function" project_id = module.project.project_id @@ -103,7 +95,6 @@ resource "random_pet" "random" { ############################################################################### # Cloud Scheduler # ############################################################################### - resource "google_app_engine_application" "app" { project = module.project.project_id location_id = "europe-west" @@ -133,7 +124,6 @@ resource "google_cloud_scheduler_job" "job" { ############################################################################### # Bigquery # ############################################################################### - module "bigquery-dataset" { source = "../../modules/bigquery-dataset" project_id = module.project.project_id From c5b29f39233eb3e36f73fef6c9c697a3021464ea Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 17 Sep 2020 17:26:17 +0200 Subject: [PATCH 07/17] Add readme and outputs --- .../README.md | 54 +++++++++++++++++++ .../outputs.tf | 25 +++++++++ 2 files changed, 79 insertions(+) create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/README.md create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md new file mode 100644 index 000000000..f5dcd99eb --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -0,0 +1,54 @@ +# Scheduled Cloud Asset Inventory Export to Bigquery + +This example shows how to leverage [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature to keep track of your organization wide assets over time storing information in Bigquery. + +The data stored in Bigquery can then be used for different purposes: + +- dashboarding +- analysis + +This example shows a export to Bigquery scheduled on a daily basis. + +The resources created in this example are shown in the high level diagram below: + + + +## Running the example + +### Prerequisites +Ensure that you grant your account one of the following roles on your project, folder, or organization. + - Cloud Asset Viewer role (roles/cloudasset.viewer) + - Owner primitive role (roles/owner) + +Clone this repository, specify your variables in a `terraform.tvars` and then go through the following steps to create resources: + +- `terraform init` +- `terraform apply` + +Once done testing, you can clean up resources by running `terraform destroy`. To persist state, check out the `backend.tf.sample` file. + +## Testing the example + +You can now run queries on the data you exported on Bigquery. Here you can find some explample of queries you can run. + +You can also create a dashborad connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase.. + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| cai_config | Cloud Asset inventory export config. | object({...}) | ✓ | | +| project_id | Project id that references existing project. | string | ✓ | | +| *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | +| *name* | Arbitrary string used to name created resources. | string | | asset-inventory | +| *project_create* | Create project instead ofusing an existing one. | bool | | false | +| *region* | Compute region used in the example. | string | | europe-west1 | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| bq-dataset | Bigquery instance details. | | +| cloud-function | Bigquery instance details. | | + + diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf new file mode 100644 index 000000000..e4ad633b1 --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "bq-dataset" { + description = "Bigquery instance details." + value = module.bq.dataset +} + +output "cloud-function" { + description = "Bigquery instance details." + value = module.bq.dataset +} From 6362185a0098990aed50ff40d901cce91765d729 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 17 Sep 2020 17:38:27 +0200 Subject: [PATCH 08/17] update changelog, readmes --- CHANGELOG.md | 1 + README.md | 2 +- cloud-operations/README.md | 6 ++++++ .../scheduled-asset-inventory-export-bq/README.md | 10 ++++------ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc689354..6c238e84a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] + - end to end example: `Scheduled Cloud Asset Inventory Export to Bigquery` ## [3.1.1] - 2020-08-26 - fix error in `project` module diff --git a/README.md b/README.md index b541bfcf2..ed81ca430 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Currently available examples: - **foundations** - [single level hierarchy](./foundations/environments/) (environments), [multiple level hierarchy](./foundations/business-units/) (business units + environments) - **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop) - **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms/), [Cloud Storage to Bigquery with Cloud Dataflow](./data-solutions/gcs-to-bq-with-dataflow/) -- **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](.//cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam) +- **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](.//cloud-operations/asset-inventory-feed-remediation), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam) For more information see the README files in the [foundations](./foundations/), [networking](./networking/), [data solutions](./data-solutions/) and [cloud operations](./cloud-operations/) folders. diff --git a/cloud-operations/README.md b/cloud-operations/README.md index 016a5f866..a17e6de4c 100644 --- a/cloud-operations/README.md +++ b/cloud-operations/README.md @@ -10,6 +10,12 @@ The example's feed tracks changes to Google Compute instances, and the Cloud Fun
+## Scheduled Cloud Asset Inventory Export to Bigquery + + This [example](./scheduled-asset-inventory-export-bq) shows how to leverage [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature to keep track of your organization wide assets over time storing information in Bigquery. Data stored in Bigquery can then be used for different purposes, for example: dashboarding, analysis. + +
+ ## Granular Cloud DNS IAM via Service Directory This [example](./dns-fine-grained-iam) shows how to leverage Service Directory](https://cloud.google.com/blog/products/networking/introducing-service-directory) and Cloud DNS Service Directory private zones, to implement fine-grained IAM controls on DNS. The example creates a Service Directory namespace, a Cloud DNS private zone that uses it as its authoritative source, service accounts with different levels of permissions, and VMs to test them. diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index f5dcd99eb..149453d16 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -7,19 +7,18 @@ The data stored in Bigquery can then be used for different purposes: - dashboarding - analysis -This example shows a export to Bigquery scheduled on a daily basis. +This example shows an export to Bigquery scheduled on a daily basis. The resources created in this example are shown in the high level diagram below: -## Running the example - -### Prerequisites +## Prerequisites Ensure that you grant your account one of the following roles on your project, folder, or organization. - Cloud Asset Viewer role (roles/cloudasset.viewer) - Owner primitive role (roles/owner) +## Running the example Clone this repository, specify your variables in a `terraform.tvars` and then go through the following steps to create resources: - `terraform init` @@ -29,7 +28,7 @@ Once done testing, you can clean up resources by running `terraform destroy`. To ## Testing the example -You can now run queries on the data you exported on Bigquery. Here you can find some explample of queries you can run. +You can now run queries on the data you exported on Bigquery. [Here](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery#querying_an_asset_snapshot) you can find some example of queries you can run. You can also create a dashborad connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase.. @@ -51,4 +50,3 @@ You can also create a dashborad connecting [Datalab](https://datastudio.google.c | bq-dataset | Bigquery instance details. | | | cloud-function | Bigquery instance details. | | - From 5808b181d5f0ca749dd1c3542de9b67db76b2a1e Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 17 Sep 2020 17:51:32 +0200 Subject: [PATCH 09/17] update diagram --- .../diagram.png | Bin 33936 -> 27679 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png b/cloud-operations/scheduled-asset-inventory-export-bq/diagram.png index 1b2f71aa5d956a4b2d9cd2772eec2e63d735f309..b91591042cfd491a78e9ca0ec65966c9e765df24 100644 GIT binary patch literal 27679 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 z zJ-fUrXmmLBaxNRkP&jO4Lt7Y~SXod0$OKBm)}%702>PzS%l~O|?FqT>zJASp-Im{Y z*~Hk~(V<#?sA5>tH)=lS@jb?=Q-640|7?J5Eqbck^!#17Qbh^udKce(p6NdOnAto7 zHM>o^e0tF$0MJlryzf1z>%Cl0{n-3iXP|G*F=?$ZaY^1J0<#xaR3&Mt_sd#-f0jOZ z`_2fcRaaxR^;0^GWz*mS03Pk$-qe*>mz+p;q1%3u(jyIyI`k>CEM+rU}vY6qB^RF(Lm{jJ6{edN5-^DlVctF zw`a{woQ1ILw|nk3LY7d^1g|T)Tpkr6YNne`q#5=1ziX$>KPes3>#iTZ$RX7thBFPt zIDMt|q2I^&uS_jL82Eq2EAGP?XOdqt_txt}`+!m#3~?gf>i zS@RYCeXPi&mc^OhkOM$6K(P+(ck|M}zbI>YfBYJR%;z+N0|ez|?zb%C@g;3gc|Vq4 z-)Tk42JkW@k}0@AQxKK?_TrzFH1!+(8`{;OAzM*w3Y_d085&7aj=%oclU$@`Rm^wHklmd?WQXaF%6 z*M<*Syj)uKufKo$@^~_H6i9!Z6KN|tb+Ncw5=I4xFLESnfcy$Lw=(lvTuM?L84h8S z7YbM$4aUN`%-m@;eU1XLstezUi4Ujr{57~=Bvf)!7A+pP7gk{SNnzYTOxJLC_Kg#T?Rp65<7fXZ#i}DQDF|5=~Sk0U$=cpa5gZz=y zuT5$k%BM;vQt^Mr18??m5gW7*pZCRsz7xE@S(&aXJ+nvRPxhV~=!L4NlUALx`#Nh_ z=k`2Lz#As$G@ieH?xwu=?q#X+bhlek0Q{SuM+GdsS<@w$GEcSo>d|YKp5M_B2TFD= z9Q13nv~dR!eIL7Rl~KOQKD3;%SdOcq{@Ft%7wwZrZ(Uh2&roRJ(g?=k#BfmF*A- z5BYD@J`KCg?XbKYKw5e=CW+@0X=3@!tN;2zw4aF@X?SkT}MEwfI+wYqmz z?W)?n<*Dao8-zOjv>8N`H|E�ZHf63Lbm-DXH2m2&0kXCpS@v+l<4q1E%j00e@yq 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 C4*Ws@ literal 33936 zcmdpd^;2A1)aAt+2ol^axDy}lQ001OOiLXik01XcS5O5&K_ZboO5C{MOIeVyR zI4K#rlGr)enps$xk~q2BnUa{gS(pKU+j2#+b=+<<($}|Z41UNi2%=!}ZqP>e;C)_c zskw*CLfiK?OjRXiGD{zc7i7HEWC+Po;u@i`KHbHo_P}6t~wgC0!+Si4Jdod zvpqXc2ainVRNj9Dzigt9fR(#`du0B7+gkC9q4-X(;&2xy<<)tMDdg@=nbmXOF^cso zR>xs;PRc`GOt{nf+%f9%0NPvr6-@h__VV;ed?POF033QH%?O5Tcm;DRflb@JDzbd_ zn!Q95x;8G5NPX5Oz>ZNz+^@@P<(Uz8oO!=Yd_Ox8>sVEOsd7f%nr%)xz#+ghUlp{m zAMCH0l}K&Rim|L^o0WXca9s}6s59TnxSrUV~C+$vPu(WutR@HOw#b5XkQ0qEc~J@Ms5d`Ch9827P2wF1W3+-!3O$ycSw{e_Hc>Ee?xc3U4ru6$(fDM6vj96`HeIZ`@ zJ7-C%tfFB(-{M=|xSq#Z#XaLo*lGfqYMRqWMcmH~-{uQ-MOA|*V#S4^Rt`meMzQRV z8P-mjR1ROq$0n+4IycNG)9V_KBYC^5S>H2g-!IPAVv10fr>87eAJK40ZOx~U5oxT!h%~@)S?`6sew;oG!oR{+jE)0b`gjHOE(7<`ocFon-2vcuDwCE08U-~ zXRZo$CyrpDQ`I2Oq8bJ7;LQSS8}VZfsZl`P-;w zN4VYM1V?huud9*I*$~%(sVeZLMGPxO%7!7cbyh_j|620{*9?P=&`K2GBPf2C+We`Y zoWzbqS-93QT1)!4F+Rd)e^G3|4VWToVf^vIK5`RUd7^JAUDhlcF63>T`#BPO=$C|j z8Y%y&eTEI936h>v%o5mYG0|Db`9#)@LYaL0>XKvCg)%GPNkKk63O>##S9qXGp) z6qYKa%0-#UpABV>r)zYnIt;%iEntBUU){>lZ5u+e!eNulXo1E&Edh;R#%rAjUTv+- zQjAJ}_*Sn}-LR$Ch8dm5>2;i-J)cIuZ6U*}HQOOeVRblSds-z-4QQH)-E;e`_G!Vd1Fmq zoto>|b~bVf7&USiXYN?$#Wm>A>te&4r|=s3w5!{@sSfG`wmZpN>X`ruQBr_R^U%`!=aO zA8+~<6^4vWc`6*a>BY}l)JH1x{n^{Lji}g2CJ$e7516G4Kb3n=38@f|G9o0VXnl>t zJL|Jr9i5Kt2@TBAo_?YX+5)dn*10z3w)dR5Z5K-lA#8jpvVi#12Zj9Gq56CM3Sk4m z4`fhD`YpNBOBm@4RIUP?4{?1k0>jYQ&(9-`p7JZkW&0Qqbn^LR=+b`P-|AV|10_4$ zKaImJ6*U+?+Gp=uxefnB;~I(?)n~OdmhGdvb!T6MRK4E%OmS)GJQgxw{YG71ha4s1 z8EGZD|2;hNFQ4Prh$12N<7p_Lns$*axi23ZD=MkWa%@G=OjB!T3Q3}y#l?SI%vZo= zbm;bx^Ct33Z5moa!%1=t^zmY84eiaec@B2+2z;bW=7`i{*BS70Tw9j%WLs=Zs`xGr z6>Js6I19He9uR1#(NZ8+UhMEm*M!(d2KsaS9z=m1-zzC$6MiP1Sp)YJ>MN6z9HAsG z&hZgD{<5(9lcjqT|BN1pu@pts;2XDPceLzrXc!@B*DBs(iqSLk?Hy`g1ku@v={I1n zxL_TrTU#UV8leuqU)NH64@69)0~pI+q{`MY zfcxq@Ru6k%fr9a2HTUP@_~1qui)ZG3Al$eMF8pWJ-uIU)glauh#eq7?LOxz)rL||a zP59QDgiH2Kvn?0jG@n{-8d8VO61-h(QH>_`mUV*B5%sS$h)-maa5>n|E*T;x;^z@! zvs=^2LsEN4z4TenO>@*y(Jk|3W8XX%snQ~=eY77^$fOSmTG);WSaGT3VL=$k(y?lst>@odeE;i<58O0?`kENwyVW`H&zAd?3TzZtiWb#Fg z+t&V%6y~31DqlmcA|@vL%s;4T8r@{%AqomTuu|0@^K_~Dg~KLx_+Nzk}LpK?QDl@PO8`ReIf zH=x5MSjNiqv|2cwzW|i{PpgqijC_jS>uxZvw=&jY2TZG4M<@G){S%O1qCSkjxrvHH zqi~lt*~Y5{zO$9h^A+GOc8F;hg+P4Qg|J4_;$MNce;(N#Me*-5@OBcKjsSp&{?8Kv zNXx)^pM-Oglof^Bg@?z&Vhwwm9|HgqK=P}Qirez(nyVK<{}SAVxtVL#iCl+tlF4x6 zs;4bZ0MfvspGDzMR4Kn0R1#A7_APG_$-{9c=J_04DA*l;&vVfvilthQ^P;jgUe(%VLz7Vn~(p-;0eJ=B34(ti=-!A6Z9oSn6mbaQWMJoG+`KOp^rX;Scl$b}uru5>K8 z)BE=67T<3H_$Sn$oNE^Rq1r~9hNn}3pcNP~NA)U`50=+RzFtbL#~zZ39*ildq|kcm z|7Af=HF`p{P({1rx9@^XP8~Wl6c9rlIM`LscUploV57DVPYP^aKW56AtE$Qx=ZzAl zto!}Xsx0`pH~dV^9*EnBv<>C4=b1SUkQ`kG7Psqza73-3?LPM(&Y1>t|Rwmyw( zH`0WCw%iTbG!W;^#Y!ZH2XKr7QKd>ybrv*Fx(I=Z8)6AiEIGVjJwMyOapiftSxq6E zKksy}wDVk^rz_~`O=Lme4L64mh&@}%snQ+(Q<>|rgAes;tGQYL+XMKaxjj4Z(C;Ng z4+ps&bf7d^Z&)znKsGFPr{s25lacm0FX%Y;BuJV&CVV8#uP#2cjeenHTfCggI**PH z=R3Qnp{8H1w!bc9SW%IMA~nj*V`{e>39K>35QT>hAWnYUbz;}!|87P?oZcPq~)|A18$6R_F{k*7YQTXaUS@_&5w(uT2QkRSgm#;v?y<;!Z*h z`c`W?wzuX(B|p371C+B1<>ugvmm30K0qqy#(|&a0x1L2KJ6@5P55EMfC^y8S%q z^&xm;BQ;|+T&dM^JT2psdmUrTgG}XDM4RY?m4eZy!U6*h!GF&1Z!0^a+{%Hs^TE9> z@V-#T!OAk&TrH-%f@2mUyFs*E8zz^_Bfp5|S?0dT9EAgs^ zu5u^4#zy$dsYmwe%qCi$%nTl6!)fBFylW4`zgKqpzs)2`Vghd-PfKB8Ba6kw_y_u| zZc~614ZxK-5zYVdEI82P`{i}lKlv9t3JN=QSicpe`Ij=k>2F+ly6FF6&Y{;ZY@qPE z$I2QQsYCGavmGRmje2n&i=rbrmR)d$8g=vM`d5%26=Se~7i8yt&ebKXMzheD&)|I8 z*1(v&M;PhOmmVS$d=96JRqo`=9kQSvgXWz(ZD-x{>e=kI!@P+oVGsyHxBaKm%q^na zZFa_W(}u2wqT*7O{4t;F+k{a~qYM=Y3Ca6m^8T&a%hayAcdOdfUPcPO8ye}Mf*=v5K*8;Z!SIs5R%0YGJeOb`pA?B^#Y48P z<*wwcIPr)wniLGpyGnG9w4gh?RP`;*<$2|!u!&}6S(!kZzJj(k-Kc{>YMx6~EO|+7 zDJ@gi(4K?qqKO=hsmvr3b!B;J9C@fxDRo#b^L={zx5^&nA zU)dGatSl|%_TpODyMu0}{}(H!s)X*JkU$g--wOgb{f%I!cq57ZrCMCCLc#?@VK&E| zzlVIFkO8$6FfwdL5|Z&i??;kaCQF|`cy*>NYfU^daOx3bn0(h&L}qhy#i4?H^B})J zgd_G&OLf*zG#PO7=&E0_=_Dm3?JPhW!x{aOa658xJ~uxv9hYypybEt^+y0ss5^^Cp zLlW4$_LtA|zT;`YQy7$a)UmyDpzHof_4YDj$?~=n8yqFzVQ6^iN$eG8Pc5CRGG8yv z^vmbAy`=|PyzP)|^y--XM3?ctJQZ71RMb*ykdD)tKmd3yZMAtB;M{m&zIbyUux-0s zwwNu1U9ticl(jt%_Iz`_b3>QrvZTs+cb@*VT_;~!iU1@gsxmRT(XrBL z$B|b}`Yzbo{#~GwjbkPy`3Ij3IS$j7Ne=iR!g6GpQ9t-WDUc{RR;ZemWcd>;p?!dq zBpH+Ql3CPJc6cOj-wS&=1rS$cyx4* ziD*U(T%PmVbU7bx3PrGFO*U;%PcxRaMYBor{(tzKu@#x;Dl|`Yx8+`kHFidUfDctGeD+sf($48jw1&dF#H|wKcD4wr>bQ1<$$a9-(v-;6T${jM1p(j@FYea%6^5)b2IwWz{is75;$Sd0M}75Oh<9?Lwdm|d5Z?S*=Dtn!|u9BW!&FNrbE&Z)jNY~k)baeEc(NQ`*-;=f6?YM^JLd^t5EEYk@4RSa8&bRO1uAWU%p!zQPtacd4c<`w`b22 ztL~tSC$9*Wws9A!M0??(CTZWHp)o0m5b%M(>-#xJ-7e*>(|3QVSo6GNcr-g2=NyNd zkqj2OmJdQjdEO&Nqfn-dd>d!rbYLWxOyhB%xn}ATgYT(A_>ZWn@Qp=7$W@uF@Y$Nz zeSHb&~|sqj*Ik3{6pKdYm;ULmh9{d__pP3y$wHuQ8P}hTxZY9$_pGI z59+=_EF4Sa(>9NAbeT_7A8R>{+c^M+YTFHG&2$$)IsaUjg=P9NNHn;%_MMMOzUmn& zN?K^PS@QS+Bt^?o8=Fpl7h6URn>HL5&Z!GpD_IA~HCqoVS9*JYcWO7*)|W>BM?AVF zW~L{tf12c%D>yfbEqIJ&ROerhylsE^BK`IF`48~je4py@2`H8Kt8a%gj;%uU^hJdX zLvlJ^X%-S>J_-F zmPVDRMyn~)JA+XcCFewAUv8eItCr4$!g9-3nuAbrV%8VTzNh7q1XiyEz}}p+IfE-1 zR$TQQZ!uL>+0!cJ)0p`$s(@~s@Q8>Me(S3VUC+|eQbYsjt7^DU!OA5UlZvS`@aI$5;-@N&A9P+u*gtj=?w& zY!I1Ej`$y>l$5GOG`V?Q&X(G2-!2SAwEtXNG@Y8+{8f~)aPT5H(3y844eU3v0IP$< zs{Ys3NjvAG=S@Xu}M3Qx>X@PvBO{&X z^1kzI3?2IaPzM|`0V+N^19W|55R@M(v(IU9#;2Ba4qI8d<9SiOPF<@_f5!<-O@5!5 z>fvu>&?>Ul+yxwi-$iGgAyG|`EbsTx673xSt$1?u1OAo^d}?^<`f#%&4w*UEXT&r~)l*Y3sNwxGG!^CLmYi(9YXjqfsCm;k=;(PCt)-=^^VWLG?+J2f zix!w1DwY^%sXQs6k(7Vi$cq+u8VZ_a3<-en{@UW429 znEp>jqZB}ps0@i7Qf1ii27wvBJ<$M7e^ao;w=$NySTn9l_|5>@tE|6o9psWkRrjGq zS>waptBZ$m6p~4%qOtEh`wE47|K$SY7h`JryvQOPB5fuE=l6n}s=35nJGc8T0sViI zb!`Zk|B-21`;~&qBze;-FF;pStbjpLt=s)BwnwB!UrKr2B}-uQV$9}y88KDI)PC&Z zX`&fty0mloT5Tm7?EC_6EY8W%+JdTFBy&3ZPQK)#ic(Ue{11n-?_j1QBx!2j>BK9A!Q!T ztuA&unc#!XBV}NpH)^zFbciNURgL86+PWib^S4q1$-EsdRP`KbJADj2$| zC04K1UtXX2PsOrH_`ZQRohdd zo!Vv!y9_Ih>S}5&I+c^18t=fWCT!7FUtd<0h6soRf6!_1AT)mZL&*EZW-@j)FYERG zTF`Xu8=FijThGD$TfJGwk_jHCjZ9S^G%8tFHZvXX!Bmb_=E?U3>j%qAPWMNcm8Nq7 z0h{{aIWuYjQt}S{4gcKjh%u2im-C9Uin`_U)(O1K$oKETIpg!U(MCd_c>%XgKfO?R zI$tei_45o?Fk0SpN(#=~_zW?M8PR84B*kgPj2c2-*RI>Y!c8mJLjk@KQBj{$W!_n( zqO7W_vgBxu7bbAA-FYQBJXs*Z?I-Xl->p-gu>bv-gJBnnb!(y!)Y**LJs zhpsQ|>Onz4FnTgH)>ck@Z!=}?k3k7&S^Dp6m{y2#xN%Q76;3Zc-SZ~%UVxU7 zA)dUnG;P`d@SD#2xpBFot(|tPUZr`zhBAtem!V_V0|Ss#QK_k^tyujLCw4Ec?A(e* zd1d%Qe>2_1mw-xq;Ls-U^w7oYdJY3ws;`P6>L(Er{_?I33h(4BIQOzCNS~XH_Kw-{ z!=$8CW%CmJ0pV^Vj-vY$$NAMIZ*ZX^E#Jk|C(Yn%e((cfUJr@%Oku?emiL{hsA)qb zt1u13M=U&#y$sD+rgi|JCgm1)$nv%Lv2bGF*5QwurHv{eC@K~xizi@q@YnmYh6RQH0`B?u|5nQh31AEcUpx>hY3J34Cjncj zV7g^cG13S)!2x`!9ndfUGlX?@rdw2<#!vcHmA$Br^jhIA>5NyU%ZwRMEE~YxikzRoaG8 zrV0)YZb*;T*59c1KA0fny-Q)(A2rwg&Fy*Az*(U-DnR5e5xbYWjV4v_(2Ns5{1v3A z+E80-xhMY}F+7~X?D|yNoRX4KgMOb;jSNk=S*LP4E^hui=iHxpbYa-Lb566lp{Xg< z_+j$E?1X(HzEVpMt@DH=+00D2f6gR_Uy+2FueVtxme*uA;Agzgk-*@e;3kz}v?fZ3&#WnTBE`Vx-xhUR=3#ofL@xBnB+ zZ6r2RaDmQRz3Lfjf#HzE@@2OZgtL%)mQyBDAVAiW%PSG{=;X4To4_Go1~Y+c+#b$F zDyd}9Ks=xkjOipCLN5(`1?4oYprX ze;(EZ>zZD=63)$*zV(>beQpM39){0y;vf%ZT<S-Jv#!hK2KS*~># z1gSw>w_#v7T-+l%JeYz$SXo#3`hM`0iMh=1cQZ?CrS($dY&La7jriN0-8&qXOMd!i zQ#ty!8~_liK&(#3$If1V{~&e0o%oqaUGf7XtmU;8@DHkJ{U>Fox7G~ z_+PnX$nnq+LlG*a5+Y~*LiXQhq)Rup#qE}z27pfx|JiH$3eTW_^%|;h%Kw;?{|^lK zWccUxNdmbt<#ZP=>v(NJq^B-z^rPl)Q$*2&=l44lT(+pfRvasn4oodNjIy&AXc_W* zN_KY41zkh1T_4$efQKgyd`6pW&&d%2{lLC#CzXs_vk_>j{efz9Fp+6I z5Ygjix302ZT2_XSho_{ZG~sY5M8oIxU?(j6dr~R!t2y;)NhuyKu6t7SDEH*Zh?Km% zdBm0~m%vED)s;i?XktRbO?a6}S!t{9+v`?u@Xnf>&--CNIDiuT_U(}Aujj5yTXKq89 zBu!|!1i1x5>gEdbL%Mnw-~Am)pg(-p7$+YS8~?elq|`kvrME^+{lhyb0*z+m2PfXK zpbdXA~ugISZBtyEl$}r^HG6GxCxteNhG?FhI(u z7Dw^?Rnhg)IaYOyljx5lZO{S}PO#vmXxeZ31cX>!0~rL;Z&F7qFN#ufxHaAiK%;7CZ3%Mcg${ZxQ7U{!>OK)on6GAF`Mjkp8|v4vj9 zy24J%TSEN-AYnno9V!}Wz?{mms`%$Q!!8dLq=FjL+^45Swk48Gl^!x^f)az^Q7Dlx zlBPIi$Rm5SLWMa()yU^?>*XA!%41p6l-0&VvT+K0V}(78jniq)-HX(Ke90rORjf=T z+5maZlD4bVLf3ig?v!Fn3>#m^4an&o;lnpQi&MB zy7KAJY*|JwxQJ?X-klqFg~NKa(bLyPksakZp$s33?|Kr&rS|VZZrqsf>)vZczgt~q zB1UO|yzPc-35()6*27k|i`2=(x^N}Sbg33B1v`~z8QasA0`9@KhRP7_POuct0iOfi z!T~XNz2j)%IPV=1WARkFZ$`t@nLtOq-1*%~TUp)fE!g+;dP~>NW|-b^l0n%On+*mE z*o6GTT7RAVZKRT?Otoa5YOZWuKn0r0;3 z-h(0)1V8|3cJ@z!tHdnVb4u%C`jO$q2K$|TD9vEH<91nrTN6I}w>)0N6OYZ7>gQCr z4XvEUiszc2zfUQ-#iOuPcwY|gRr&E>V6~A?*cnNn1A*|^2#KUSjA~mKi%6eryYgg} zoo+5Wh(N!uf`zgj#c16VkUrUq0wq&0^Fw`7F{R?{=b-V`*M32O%I`y3Gdho-g zoWWBckMfNc%(?eIF|}@`g~N@IG#ltFwCPVFKtwo3QQT>gevAo0oiYO$3$iyQQ0CO! z`8IqK;O$S<3<=_XTU1@dTeH-p8<@nkv&!2ZDHTCO?u5e*xSssniKHOzc(D;n#QEZp zGM-qoTbH5tY5(Q!$(|A*b*emClP4Co8(<%}6ypju!MJw)Q>url#Z&pr^L24J@l%4( zrAiXX1;HMqC0@UL@D2OYn4lk8Hnd=OEtH38UbB0g5sSea1dnpNPt7OwWO)^tW0~d` z!Y|qPNj1*Fi!d+pb%EvUk4M+U=he4FJj}zLp7RUh?p19>&3XFO5PshyZY5-;)wu;K ze~6E3KR&mK&w>XObjxVQU1_G{ZJ%B;e4;8hEZ-KY2xU8b{$?wT!4ekRv-A0!nSCbt zWy4+W+ajF|AG04-*oPX2CWlUze?h{G8(JumDV?Bl-ENtr{fUi`8X~dh>O)0O??IFj zNYI`E4v2oomoTTay=;Tm#BhOf)edcxn_$6XU zthdHf%MSTw5d_)8oio_Uk~T?&@u`1LdV(w+-oIi_c~D!QCti44n1vwwPmiG0#9scH zBRRJ}vNTV_N1@A{zN*e(T(+YN9Is}K};F|u%q-|9@3@n zA|;9A(LS6VWFm~8AE>Z2hIM5)I+W|6Wgsq>>L_{h8!<~Nfv=EW+M`r17JQU69h-`P zLqa)5A~KYd`M)f(K(`TjliX(=7PO=@HO^fJS@b}FqM9x%NOO2q5ux#CHtSb&1$(rY zi;RFjinAisGRPQ5Maf7vGYs;bb>>%uDC2}ghyXJuVy9Jp z7Bis487UUgzFY@OCq`|LZ{dZOH7z1DVI_Z^=M?3B0x==~QM*g)Vq3rpHVYN=X%<(AXeY=*cdwCp+<;D=f0#hKV{<5_Hbzk*4RVZ6CqWT^vHguRmOUr6blD~ z21Fhxu@IHC(;Kq)u(zm0(M7q?+{f8=<1L!uNUFS;H2BP;!>yGAJ#y@RE@62(07$RJ zcUZ@#JkC%sObf-rzjv4Z*9*X8G$)GQ>E+}aROA{`F|PZ!La*VbEg}z-mSyk`->1I- zJ-`Gq_cWZdOE$%)zqwul@EbW}@^!xXnO^y!30JT}=dQmgRdjyL>O2dlXovN|DwY~I zj{4EL*qKFG5M-ocAdzdrvS}WWrowS?=AKcXCP)RKi4124&Zg2%@hZiExE023_W8f1 z{_Zgm_qokarDF45;|DQ*{@Q}z@a8tBYmI4>9Q=%458;|{S?_j((54Gt^q##k?sC+js$ zoWOOqx!f41ofX~uX$aK8(qW^-wdxhm8UFlEg)@+UwXjg^Z1eF@P{Um!+HQ*G2#w!) zDpe4aV!C}Bj@}soNsxuCf^670OXq$A)y5i814Z>A)cR5J>89nQ{9DXkDo3&I$z;^t zh83^I+F?^rpKOZv?GP6$;P$yw%Z}u(=VTB@&+#IC!9`Th_4)w>D2(2}x?V2VdStQt zObJ3!6MDOYn31|c03c~>u3IbM*qhd1?5S&%C|EJ3*Y2e1<4%YZ5Fae}%XQ13q10d+ zTR&Ni`wKq5_nFcR$rb->6cZC{O=hWrWZMsehHyXk~2$pfp8UL11tj0t)?UsKZ< z&9Um{gEg}IYHt30QUfEsemM<51tN@Fs5Z}GB| zFa47`0KJXpYw7}L+lQa(2uKf^;>au+Q{Cb^N7#pC1fh`(L zOiC=~pYXcPYJaheP2hNIt`Zh9~ zg#*@g1E1Su#hE=gYz=BQ{Bpdb%_?vXqyAC;`Sq5u!s5@5R2@z&+|-#Kht0iPFI)a_ zq@3v^BCfB?YSB}F809N+tMYd(iS_T_1f8F%V9zH5?|LeT7l<*OJ?`OgC%Qunk@8SF|=f%_GQnsKvjtiCd<*Zd8_*SkKLhwe^ zk!ksLps|07WYFn3ip0n6C+ zaIPjh{{xK~cQY;a>ToW~acRrJ{vP|)ja4$7d=`$H6?Sw>2pUkzE6}ElZTeFz7j5t% z>F=y^?l&Wi`{fQ3vLpYj!xH_5H$OHzgZvhnvb2EhIFH`xv11DsLKj}e@nro|naq`@ zlBH-RC^@b}8W>ftBfYhwkAZ@p&^OK(j6{Z-*^VAc1t<(+x+i$_Byt)Ry*i}J>=7Y1 zjf`{~iHriIgQ*|j-u?kqD*gkO|g`Fflu z($7x2@LkhRz~!&rc3lDzGR8$5j~qP6FVx~{C_#?+SD>FN9h|}6ItMIM9gSIY1Ryf?G{J%TPYYTH2Fcll>fo_%wYHC1(8n!||#t zW)cZHcV~P&8qR$B-=RF1d7WH^WfBKk4Hae^Q1nDdAM?=weB3OZRu#mWqM>v%Y6KLi z0ZSaSHYb9ZuFBw&RL0gymNG zyaYMAQ(rIP*p{*`I%pI@H~UHOW=@Ioq}q3&!F>8Lrt~_b{+gFaZzl;yCG}!NJ|Glp zpbt@H?|f31C?fs=ajCx2DXJ50Q(W|QAaq}fSwqB1X5^9klzjx&+ZfnFi8J9o9`n1|!CAMeL!-)$f z{bN8+RS+Wx*iEP*bf@nV*5def?!w`x0Y_81vl7KjDwuIsOg2~zZy&6FX49a+N%wHR z5{^92t(`1=S!0+uZ|Y;~2$h)T*i6pfkPIzIAth*jwnA?-Yb50u)mZKu?F#0eS)cCC z^nn0`1@klS!p|O#G|n(=t1g)|j(pvN-URIj&ms*ucofMK+U9jyEx8R;xNe=o+ z<%UQ^+)zW|3w2+x-wOi^BQ9A`-WmcdAo!uAhUV|Fu4%&{tq(iQ_YH5`yvaq{@R*q9 zulfTyNs2<9t24(2;`vJ;5;~RHXz&N5nw-0VC!=H++B4}$iZW!ZTj>*9)*n@7gUAOV z>1L6V(r1|&83cqz{!s&TyA`i$#E!!^u~5pMMK@BXdoeF|$iV@1)5V*Y;ohdJUw@$D z#D3iomS>3J(%tq=E@oQ$=Aa=F6B;2K|5*wbRD2(ri$}D__41Qh57bpR1j|M+`L zi+oCGS)^yD?&_lN+=T)!sA4ac_CA%5^V!yxX~TCXy>mM zpT@n}#5*HS({IaMbsgZlVcy}h)!@xb$090wG%e*T;o(3HG?yVF zhKCaE2+87XYOlbkk?7 z8nTMuQXK-L4a&)f%c`sWDqNAjI^48(j1K;FM`)V)`E9uftG)j7Llo&WmI9yVN%B7I zZ6_s7r?aYn!|$=!(bwDC!nI^1`PkHx?zrfN;ktok_>gR___)LlYr7fhyya>OFht!~ zL69Y(!*t-A&-2kwPqPB))vN{=`+~5@5rMAXmJ{62%M%_!vB>zmfZ)M9OsF8SArt8r zp(N~r(5xsKvtp=C5?Qh+Kdfn2NnCR~IwCqDL;9hX;#4R~O7~X zwQ{E5ypqV{DV~Ug#y2}7oyTZg@qqz2-0Y{# znXC~)lRw4^DqEqcd8i_91Hqe z$v|5i4>#Fzb~j9;i_tAS+qNvCesBADjC6lPH4EV--s%0c=d5(R`O#ua6rYn}WR|hz z8ijS1XG1aQL}%V-={^o9gOo=lrk1yuQKSG(u30Uu{d^;#7q#*oGI>oY(0g}P7 zx0BwiuG@WOIw#HB6|lbD+cP3LK$4^W=JJa8_Rde57>uKEqn89s;JZy(>?Wd3N@eEkyJgd1ytup?`R%= z-|L`*q;!hyTRP>+r{2At_y0mbyYjBR?>LUx@~&5J;k{)TgEDWVBKhTY`pBCQ$PSF` zGIaR^X5zP13Z!v4t$ro-gw$pZMDf00`gNe^^}V;r4DHK9D~;m$F2VhcNon!w^;I1h z2GvpGIRV%8)!2zR{sUlujLY?Q++*ARv`YJlWgzO8(`9q{If_bevz_0Zw9O6Q+cfJ^ z=>7Ve!tX#{TWYtbdF-q_$he{nC(Sp8GKVr7yi+dmG6$kb8m8QOpO+_GRfRO|vU8)hzSowZrK1}i9R=SUeQI61>V~0z(9?Iw zF8MvmwfmIq%KSL0!owUZ_7klRQilzbD`5r-4*3Hik4nRc)FAv)hBoujkg7uq{zb{WE*qmdYTaWOh-P{6=j|r$cASs$_ zE^L1LrrSdK%9Fu%qHzO`20HcmpQlv6)~~vin|QLH&bs*baDCTalA{rxl`ji9iS?-U zJiwKpZ}Gy%(eX8Wf_Ur0lm5q&!Wo2bmR8&yDz?isg8e5Nk zBvmE>TK%k;BBIEeBH{)LW0~P<@P1qtdWN#NY`%sm% ze=A`&+S27^p{c3)z632Tyo0XlB+RZ2-}jqH##3gFg=f7^_}0gvLni#TrNkF2l3=iI z`tf9%O-)z%@$Iz-`;fEQ!rwv2W6+dx=i(T_*Ng@Z$n1q;MS^N=5^XA@y$*VFcRWUC zl0}elE*baP-mop2Y&i+LB&;vDI61UBayG~o<6b6s8CjZSa*{mk#W+qU{(r#E=AV*Nb|=kBy7985qD z0!6P_hyAbB+0gj24RMp!xUM?74pG&(gU&VSbu)Xwywfvhh-bS`_>xyEl88^Veq&1h zj{JN94F|e)6b_?oZCWdKE;7*<9AaWy=8T@fr=>&Ap~}r4rP4agd}kc5P>Jn-`g;SY zfj+JfGKd|8V+_7|BHP9#IfDEMGv)V|_qnYT%vtVM9#yzVVhr+S^vpN{E$j)jAT~r$ z)!5W(!FKSn&ntVZwL>j+Wx|gg!>JdpBl)rBhHqLX~qFSpZ zg8dmA>wQS^$s!@M_^BkzoXQ!L*%K>;WtB4>Wz2L@s-QC}Vr@-1x86PqS=%*}lc(wLd|+3zlCOfPf&8%y;fy(eAd)fCOOgb7Sw~U&P@3 z{?swEXWk4or8Ri<3-=n6AFJ>|W>-2LP!SWgUR81SL-=j3Bzf1{`_=?Y(|lrzF4i#o z^$M0`d3w5qix-O^UMq4(=BtrU@tOa_iA`Kgz)Oba;(M1hmawtP#)AeluV2j5*@~@Q zT8KXQ9p!{?Z8T4qvSBaZ|!QY@~`AO+(MgA1Qk05d~0xmgQ!Mz^J?Mv zqAC^2MK_)kH*Lvxc`zr$3;@GMXAcBNM{rO8iCB2aj&ef-PlieL3z^tlW~9~TRX{HQ z7;w7oxDV$PYX!nV0MM!M1>Ib1Pte$q=JU=?*`(F18*%4bYv*u#kXM#p+wjnG4A8or zxD+T{66$!>s#j)@yp=6va4`9nFOOAoS{0o%q#eOI>z_O<{~Qg^aXHT)In^{9Ab$Jh zpNpn{eRsL4>{+j~uLA(=TD5;(xlYpDl?j%kR5v=Vx@`IGPS=)y;Do1od7>m<0`_IslB3i}|x1$?@@H zQ;Kej!1MhqRU}LFq$J-?1-BsQ-jL89AL^1RJT3l8ASa|C2nTfd;Z8NIqTsY?bg?7g z&>G#s3G}~8d+VUMnxJoV6EqMY5C{&z-Q8Um2_Br_zPMYEz@ouDxVt+94esvlZi{m_ z&-1)>Z{505^?mct);VWqPM@Bho}QlW-#B&HYXV9~CXZG`0U7gBq3`km>vO%4Ii-^{ z#_uR0@nWf4oz3*Sw_#-Q%yq=`!;N4Z-jzG5k01O$e9);)p?asM#(pRMxxz)#==+i$ z&bE<>;t^N0=jJ;_>bn)6D2c7KIl{#RvY=t0Y(7Bl3X_NijXg3E;m)1uq`m zM%Vtey6mNz=Izt3n1{p4H{M#o6X%s*0+cFf%D(qAE1D>{$E`k{7;)T7(c=MxZ$^Wn zd{tde7_J_qI?K-leUIYDUT)9pM8qqYmfB8$&WCU+l9WMGM^(7U>T$Xb8t(y9s1^F! zt_wgjPMk)s9*xd5O{QNfe}3TqCJz8G%Mwlt#e`RSq0NoXi`zvJ7n)zyQUWmEgu!wU zef3nOAD9Kb{@~~Rt|zEtxHvHT6R(Z5Lp`nXCvg#?=fB%wkz)aFg#gr+vqst!a_GI>CO2fcit62Q`b5aF~)t}3OOfb#m$0I6E;x1EsXTgp10tS9`VLw(vFrw0oIdP@ff-WZ4yFBp;o)-{4knliWE`g>$^h}TcXGYLGw zAavGBq7id%HHgJ5I>#q6+(Uh$+Qexr^lt#0pq4hbGb2#|bGc>nz;gX~G!|J_@$9|0 zuJ3`_nu(6r&GS?+bl2nfX{bT)R>D5Hz?a_K*8MQRbwq*ltzU;A@2>aott2&DXpf#do(D|V2!>0F180tR(0AX)0 zMC`5r9y8`NJ(MR4OR?Brq7e`HSE*Tfro81AT4pf`e!lTjrYME<6xe1EE#6#kvvEcx zk&}?vOFGJX(*%DRT+wzt0pj{*M5{Q69M-r8JQ!EmQ z>EU`L#%4qx0DW1dFVilX7e!G0YtCU(y9nn@G@-L85>keM#6L2y$3DhYII_^VexjH_ zDKShK>vW98(c46NH+#OT8Q){B{8oj+PnmR4(QWTNEJb=f;}x%21bn#w-Rc<0Tfae{ zWb>JJNx%m=)M^g-J2qZXgj{lh`G$Bjq7HYp(H>+bfvJ<3EsrodDm;h5UX9d+)121H zxn&J^9nr7)2%8MC<>^JC4gvV49iqXQqGY9F+NW z!`i-rX`Nv)kzTegzXyvry#Mqg2}MCGoDgqW_}z4)cyQ_^x3}Sp;B&x;Wf}7ObgzyR z%9!z1WQRI!X4JQzmu1OYu{{08aVD;bl0d(EtFj0g5*Nn|5)pPOIyuBFJhJ0(pu2De z1lPZg9Bj@-ML2*_@yDi{Y7$aaZ~PgyrE%G)b2aXgn=SH(n~WYR%>h5ji*%iDdx`?# zi(6v==S`Nk1gLErxA}0=O>1guXD;I6$E6hvfE(Ak8VN&q25H3pF6=5=;wcAl+V7#U`k-UQ*cgSW>l~hZk?tRr7n~7 zQF-faXfBGw(x2-ri;KgQCyp2^YRAViO*nuquYD#18*1quAQkxaUD}{3HJBiECthRV zkl+6;Gpe^yq0%Y%_NV$4is++UtHbqP5~mTigZs4gynGa7M>anD?mJ*^Dg49H#%`-RoV7|oi61-)D;z(+ z-!KtruT*r>2T=gGP>|Y6sU$0}#EW%->2bAOPT*JhuDN+r+pNBC2K0onp+lrboZJ=x zo4L-Kyz!ZZM5=^xHVA3kYM0R96nuvyQ9cxSfZaFCsYnZ6?A|(gF)0i&X@~|ky69aR z2TBSC-s!%_Y{TrAmywEK`TvOp*zg49>Rycv*cUjarJZ4yTOZ$~S`|1qf2gE-jK@|V zZM@?0?kelJ#s{)jyov$xZ}ZL+6CQegCNx`o#!7Q3h|++Iq729Q8WCteuiqoMmT#hV z?yGAd3(j@6b$QyJ4*XKgR<%x5)<;oJRHieoU!5GH_>rjNCrwqdOq84~rK)8j6bFl3 z2K{XcWYlVkM~vE>*Y7Rh%NhODbf-ubhOo`7gU#k}!b$TAT<|YXs|huTfIz;+6d_*9 zGLy2V=Apk^6OA%~{t*R$VFkL`%bJ0QkA92X8lCb=Ei0VQOq<*)AaO`6rMcvJ5Qnp! z52wugq>)+`@a||f(r5nW8cSBu7g7`Hao(SNK#uhb9@`#vqp&aom=pky3m#%Z{5}cq zQ|lig9sTArk7%*|0TmlBlVBWtLbv64XqnVkgI>Fc?n2`QYRHrPmWFel?gk-VY-S6R zZUoHl)!mo8+s*;|Gqclj=ctfcP82s9YM%9-wpf*LKqd}mR$bA~TR&@#WUOKwu}4T0 z=$`k3XpjB9kn#5XHrm>qkETE%1RTneo_^E|SIEmv-xDqUaFY!g4#Yy3*I4?h#K4Z? zAgCRH`!;nM{cWJ%2x5a=OgTOkZhZb&^wpGm|0n^iM$`SRPJWHT(ig<<2FMf66Z;4g zt$)1Ms2|qpbDphrVBZYutE>Pxr9yh=(*;iG9DF^_tX{p*^ekb{d8{b33;k_I%-Hb5 zfjM-jP{&_-s#!wyhKzhXRTObO(d^n<=8oFc$8A+4Y?K(7x`r67Xnlsa4MrU}McPLP zNYp%)mjMvL5)_#Tj{W;QwH&6$ku+Sf0AA0g{JA*Ho;$@F>5E1y+C$N=*eBe>D$1${ zQ@#t@nn-VEXurz6uC00pOR4R6176cT&#q;X$zxafz6a|0JHcW}9P7@tA07=Bw;VQ? zpO$-Ld5TCl&0t}*gdmYpOP^(t|NQx7$}@kZ36chh!W6}l{{7QSC@Kmqd0CDUvdZJw zUN9NQZXx#x7!jXMI9#Drz^?VkL0i1-S<*BZM9N|Yuvug`$j zJ2CMHkTOprmsBU~4U)BpzRVtZscUh{nsU6FuIrs_dX9%jyJsf>V&Z+ylahTyWs&b- zZcGZ&X)}pn%fxn!_wFbq|NV2-!6=mwzr(FWaU5<#(&jM~?$L{8?mFVLr53;0pY5dz zb5DQ2xakYb5l?U9y77p%yetcP4)suElNZ#p{6x&YONert zR9bk-NAv?7JCOl}9dV%syw#?o#dziFvbvoBPkdHJ-^+TXbheqvrLXF$%Rhg{+EQT% z&QXo&*44aPMd^G9j_O{M_zD2pSJ$-!4fj_}bb0;yMDdn^o;1Xyx8J?xI)`e{JUEV! zNhUln^M=Ahi2`&AM60Q}E*x!Yqkt({t*dEN+Vbb$aevibaO#UFLb#p(qwnT(J(cki z=X8@~P@|dxxi5bBeT9QA(>5mPIzDRw^5s0sM#oi$u_~p0+Kg>g;(FoGBP9(TJ*;!w z=<*L|`Hr-f3K>a&*XBou>r){51Q~?rlS}E~5L-ITVZNR(R6D+aEQhlgOzAJ4l=}8Q zS)X}N++=n9IX5JcPB6a&xGVcM70OvJ4eZ(O5+@>FhQ+3TP)(QRXJ`D8J{M*}A(LDc zvmi`(_GA`bj%SOLMxLk|nh(v;i@a;_RNJ^=11nim56S#A5>xOQS=6XGZh>wotl=fc z2+w#zLJF-wjXvgDN#d<8kVxeQ77ntP`MI0v);dq1fFP;oR*I1R#im+?0)G6qIN0g1 zTEsBv_`Ly9#<^8(>zA{SbhucO;+}_hzCPzgdb}RY=I2upW;oR!O-LtsY?en#QUXOt zV?{5r2rYqEEmyCPgJxhAeK+Qz!KQPyGq?Mlxpw8Z<+vFRxm=Ot>2`7^Jw8{#$>iy~ zAE{wAt*>a=KCywT?sU4-%#$Nu8q++>fX*nKHG7BV8D-7(+)HX zGbtRi6O7q|kziMT%$Kdws5Q3*s-dnkv;l&f^9)J^3?=DX@3}p}`_u6FHSKWpg`Aw7 z3@<$NcUfw|5iPu4-9Sa}LK)2QqqIlHW3qJ91`N2R`7mtRk2NL|7Py8JG?18u{s*c- zV8g);ku94{`uzAG9E%jYB16@Dj2-R`E;bE0l5UXObu&HC3JIf!*JfW?Y-Sdsr|r9R z?Zk)9wou`H7pCCl!2Krnfeskv%?U%6ZDaBfm6|&MgBc(P{uZM|auwaMB7d*G=i7-*E z?O_qf)GsPa=;+gtbiz<4tFlbt>*MzF=S*E zEsuqJ?{;&PEGNn0W?A!{RK+&?TIKsFj?GE|XpxM|l`!`Q&^3aqonQZ^FnEn$6;T;Z zmrcCMUT)|I-p-BTJ*RxMG-4>KDb#qSeRNaP%j$0~YDf6)Q7@M*BH9`MNZB8}~qd@nGji!~>93E!{LnzU_kQ^ncWic(ye9p~jqQ@84v{lvi zw#vrbT1sYC3Nki^X;Soq7OwKEUaxabZTy#y{6Q3CJX7B9D)_9MCt71dNkv9Rm za8=U5?Bq)C6_g~el|{9h%LXh-OAgjO$tGx4SUR~$hZ8^v3oqY-tJox^tXph!W!YB(#t`crUwnvOT4Q;n=|vr%uu z=aNX*X@5XtVUNBW(|Q9((GoaNXL8R=Q==i8sT0$ohkTfdZbeSo(PC<*B+@x5t8MHc zX=tdLlpZpj;_XgR$8r&^7K9Hhe!jrPwUBxDDLJHn?Q2bqnawLq_1 z5;%+!p9<{pW@vC#|6s^yf81z$ExdnRD-WxQF-B>!KHS1?O6@n)z?s-FtHpA|H7fUX z8J@9Cw6xJJ6N$+_^Eo>S0D@C=+p|8_$m{MX4`KwtS5h&lsh*I(Bz(qCb;%Q%;%ydt znn>ko*5RYjprdou%=tmVtu@%YKY^Xm@X~FlQPz@axAeKvAo$*l`K4p`c%n3|TB zaaGeMA_SdD8(N*XL z?&BK7$Ch`$cLuxtr7J9hX$cDttJ?G8ag6^}0tG$V?4$rB?|aeIum>u%xO7^eU6H}V z+EJGg*iJ@aZLtirxD*!CX6Duks1g-Zj_0FFPJeU!Gc?`qUf~e@jWcyenrWii*H?;W zYdCC-Fu^0&HNV%AZ#hZ2*CjVSilWzHvAn(FSDL3xOb9YkZ6zD)Hh`_fDqCT}th+=x zJ!)JAeNnO0=`P>Ui_~b~s;W+ZV z-sHMxi=r4Zi`4G1dBMfS<>3F3mF3R|ThPMr;2w63#{FJ7#Z3(09cK{>exF=Q>e`tbZ#O1@ZG)cX|Yanblff5V3E^a#{5FO;85^>)>P9*DvPX z`JmN|w&J{;k(S5U2D7tLM|AWV?2emDATh(BYmdY<*0ArzfyI!l=Vn)Ffw{7Kwo1yl z_KQ!fZH$E{+^b>yYv=}iDtG|n1{}NA(pz2oyQ5xsO`A%`=UZIzlw|V7Ws-tF|{09^TAfxADw1gFv9=kAU-*JGg+uh_bky{NORt&0Ub{q>*4Y7W(X-EVOD_>)r zq46OekX{-o!2=o_OF!ATifMV0_F|`*E#tj?Ep~tw_ zsngk)Ai;nNk2L=5Yi-VNK2mUNua){8qucX?$fxYIH*bjfJBr4qT7;TW6$07(bUBC5 z`$W~|z$};d2S?6}zbsiyGD%32WWvLd2p3BZF2HKN559?()$<)D@oP+idZ%OIan)Oj zD>&e7$JeCC5w5jXp_M%RZX?H+v(PK3EI4jO8d02qnR4xI7a*nFY<9u zwE=K=ef8<}j}`kQcpiO8of~^5B70U$?t{@b)q`8m?eq?%**ZA>r^6ZBm{7~Z)>y&w z&DGsY>?qzG9|Pr50uIZN^uk|B^uWikZ++xobh5h30+3IFtK6BEDWVr!Tg4dRLH{If~#B=q|#!rpzu>y z$mkJ>y5gg@Y}2s$J*w`^{-Tm(3pk|TF!RFu{ni5xKdnXcPn_fk1Sa9@8_QR;RQ8&# z5NduuUG{#1BR4KmBN-UV)o9+v>}=zmR#3*>Q}^_DGd=s~psM_8eT|R_NNpMTlfy#5 z>*O$e8#r)n&svvm#IX9E@-wZ{$YH$Yb?#*yu*5`U_)LWqkIUK$BfHg)B3#xIm#aJw zA^G~y$b0W(seS~Lpu^-|Ssok19SRL%>57gbO~qJ{|Koc2vftG3n~Q}8irFK+#(-(= z?=2kzy=a=gP9!>5tGV|^9983hylv!rk?(awjHc@u>~zC1{ulD2q4U{5G0(v)R+pi4FarIkTJoLAk@t zME5kXw*Gc`yJobCzp?SIenUZ0E`jjPNGcz7Bw#_i;9dG~Utu8mnd|FwqlH0+V4G)# z{m9B&d}3$e4%qvArM?Cq^`%*Uy*8D#R>iT&25#8g@yj0gx z9!CT#X=0QHM@-+r1e14^p5bVSpach=DI%fg#0{AjQ3Q}O>;Us=wlduD%y->7Y$a4= zvtaeoW>JccqsZoBxjX6|HVVIaI1dA5*c~w)#ofkwLq{Rg67mya4Xbdd1S>jcK1}W-@+JX z_lUCVBL6s>`Gr&&U;8i`VHbJxmj}sxeYqAz1L`-|Tyd6;an&e8o29lxZb44C=s${< z_p^Dxodi{UOd?w=9{KD7pSQqp4+$WV+H_+Gawm@Fdu!orueT@9$rBDa8mb)N-K<^p zk2hc_YyqYSw&VCFf^EmwIx+k-`r!2aW%-PZ*|`1qh-`20bo9p-T}eq0k^{5blOlyA4P9Y6|6ayDfSt&YTswmwtpC| z_|6LrHM3q)>BJBHQ&4g9OPEtMN|~6ZgY$j~t7)63^cjm~l2t37<_b2&%6@8tT$d_N zI-DNiKP|ES#3j?np~_rw7ztL-gN+_KdS`dM_H7Bz{N`S5l!MebAU3l$Qt?q;SGgyh z6AAgVglV;SJjy1Rr=7%JxdW4eL%A9!i4@(49QMFpHNr*hq7}o&qU!WAMw5g@dMmqJ zX1dXQk^fcSn3G%PRGNRD1u~LRwgtk=Sim6do=2DS?S9 z+-Ig^rg*p&8MD=Vvye>(HhOhgXBy!19g`CIo4azXX^_1wi^HSAMWg8pM?wLrS?YG} zU`YnSE%aN8r{@>+M<#kV>#5Hv$D4^0#m~l_e+M7KdZ6*s+RW!A&Ro?)8b0LqPjiVJ z{H`~b+ehH~{;n@X+^VV5$0~x;sB&e~w)E;Fhp+VmohxQbz!Zk)ekI=*W#L>grXol! z^vH$dVit*D{HPDIIQRHNvUp`5nVPnn zQ13P*m$pNSL@Iu2sxAwOMb%6SVL?JgTTrb{geT_^Qf?~fGi{)eG*WCCEXeLJwAf_B zF1jHi!uriGSdH^l^@CA3xuo29v98yH;^CU$Z9BW-vt9B-ujT8Xp_v0@FEsB%?^C`t zMYv{-yxqJwu@m}Hk|u9suILr&7f6W^i0e!m-$OOU@kENatnfN!Jx%LS&mMVeiC}tN zQ9{TUUy1;^QQ?IBIMRSTva(aimHNp5Xd1VpaBNu3>}gx*SYe=A{M#G)P8taw9Dk80 zL)Cddah~bXMPKqpZ$sxQ^)24^x4k)`C@t4*b*;}R#_Z$$?|T;p#HPPi@_Y%IEJhmT znL8|Yo#iB`yGwvak!7M=uC3^zX#8CK?C=0YMuxm2h2p`g&7d8IR)!0(S)H_+@#^s%mq{N;SOwk>{$v@-TYvC>v*c+*k(bo;a)`3@|5m# zOR|IE&btNKpYh(!%db{hwm%1QLS21t1YD|mJ+i^03GNdaAa>vLy9^;TnMh|G2&S#; zH6P^AQ3?l~{pZDg@N_PJwZ>Msn8SA^Qbc2wg|chCVl#px&jz?-Ctp@%vnei~@UCH5 zd)Vv zh({OirwJjZC~|YMTxxI;?MD2ZUye%epsrdJF_k!7wN?XSrqb!77GNO|-Jk(faI;&HyUlh$XNK3+zx-6z-dtNrz94YK+%Hpco=&1R)czVT(T^BX6rEKt#PTdi%Z{D(ds@tgtpei!Y5U+Xcid()#7%f z7#_M3>3wkMz*C{qn9EBpqI)Y#b>;5JvV6hViYD{isy$tkU6+<9h?&p*PGZd{@6miz z$Bi!9K@Sc^Fwhsr!dlNG1bI=Z)0biV66mfL%k9LXD?$#tARmWEf}*J5u(`dv3KNyl zu63dxSMg4SHwU|%bxEG^0q2vB8=jTrGATLvu%Yn@0=%8Ru4Ww*{h)>M2#i1Mo`2m_ zDirW1r2b?u@Pm(rvjhxs1X5M4=ckT>tX4f|-gL?22EH^W5R`l-@?{HSIYcPH)6i7| zAxU7lM=`C{dy!FtmZE#5P9By%Ugw}gK-aDRSre_P!H zPdUg!^5^=+;l5|`L@U5{#Uzi zbL+&dH0Yyuiwa9w;DZn}-=D~GDY{$e3cCfWMWfO?Nf(}07vpK$8T(0?mQk#{8JmBh zBXK${qc2fA&iW?OH+nKp&%jVtUakNHZauMEpEDDc>FQDzRP6Y4DyJJBaE0(K0HE>$VBy1{4b%O=x6R+%TMjoqc+C z*3{T(SGMB*@$Ndq%XSS^`eFa_GSp+T_svbSl_LsQIETyj^JgbtXSEJyty8KNhmbf&9ugJiGtbP8#N{ookrS0-Oi0~ln1|yHLPYL1IN`sY9{FvJ-7x+ zU+0e4=3Ctzt*uKhj3b?Ru_B6Ixn}RgenG=NWngYAWN2ArBYwtoN8~xT)~X+uPtxH_ z#741a9II#n0J!^q&j?62mQ?>3G_NUFNU-%Yo3QlRSS9THD1PR=oT}<50_6Z95lUB5 zPpQba*-Wtxk!l|9UB5T42wplhFh~30EI9)7vp(lFIHe+0v$I0Y&#DrRcBeN{ZrF?_8KZ=9=W_ED>R9TJGAoyED9wuf>c>F0;TcBz z$dbcByXYZ}>mImgCYQhv&GKm(NLAw@C5~xqBBIWlaE*mc`+TFbRU%YaBI`t|?0iT} zrOS>;STJtE3e9uIq>m2#zG;`aZD(PeQfm;VAl{rA_<{5DyYvIQ zz=yWvot^>zqj>pgDg{}@*ho;tdp`W1)D<)n9O(~KoFH4kIbRrZ{EJil1sF8VLE+(& zcHMw1r_^aq{CzYIi>yGhzm9Q3y*JvG-Dsrb%{>PR3ldt)TL!tl^bqTD7uTf7=1M{` z-qj_E1jHEuZhG)UoJe0Fv$tzJk+BI(Z2T8KOKZDrHv>bS3+Tc}99D*Ee0IIEN(3Ck zh}eMZM94xi8K5GE%xU@2!59{X8#f$D0`_@VJ&GDD8U@e{ANC#U_!Cp!CS!;PjQ?60 zJ|>)BA)WuxjP2Cs_F=f3=$kMb1K=|hj+{dgTZ9(iNA$iE08jwPqCyX{004xcm}q~C zF%)zSTK-oq{fzR`evjFWf~Du2)7owQSZ?|BWJjCwEy7uJLQXdzZHXR|P2?$Sjg5)T ztX{G%R59cS&E?dC2aoDqW%KEW)+4<`1J4Y$4Uca6>OMY#MskSX!=?pJ*`of$Lh&XE z2)sfoXpF3(aA1syz?gC08QsG&(Vo7(#gki=S5GL2V9e@PC|6wXBAd$ptF-0Dp3)GBP+y)j7E-SfHmQ~SKb5}8 z4!Y%O*Xk!1F3WokLIv(83iK_R!3)hQZXA}{EEz0@{=2rD7@t_-{*fI&B2^0mc25{k zB&?0Gq+cP*Ej#xDu4|*F?lKx*jz7$$!RzB*fZ0OwvYL*Nd#s*^FC66KjKaX zuYRAaX|AtKHe$FrBU3}aqw6!7`e3-#0DP#N=js$~%Kn2RW=qo!faJD9AND^s z*wj@#I7*8C{JiPUEs62Qi7W{O{a9%EhqVBJA2d_@|8{A&7=toQs$|l}ZGJ@qeFhyz ze!qGRJ_kh~{z?A#`frW@=W*qyBH9V9v|kfb8+r~I@6 z&voibOeq~s_x-=kAKtFCi(~tTP%p?p^v%}^3ZQXae|g)?(GoOCSeyIh*f(Tib+&MP zzu+AYYdtTN=28E9s9@+OewgNyx4izC6YxqVlFF^itvWkl0j2EkrwNeYzn6)p|FpTe z4k}6`BBsUt5(?D^)o%d8Xq`MOZ0hAac1*@1xopkGKXVVb#s1~QIGZ`I{BS~(=%$|x z!ZckV+#>TFw86jRcwppwFCR9ZW<<0T>5lzs{X77$ zigqm&7e}Q-k?Zm!{f}+YP6|91$_W8DIae{s3xS&b|23szFXFm8=%e7RLLLx)1*Peq zum5TpzD%vE$zUJ z&Q};yQAC@FnL~OO^(l=_#!^7Rj*;BL7rW~4N=7o4oJBHxL+3v^n#+Ucucr=zqh2VA z>)cQeIgE3+Qd_;_#bN@Mi2G+2gB;seYjc)Cm6W+C{ClN3EppSU4;N;R7{R*Hsp!Ju z?Oj-Bj5Nt!8(0$};-`aW9qBF3W^kJ67DX)0;VgOo_OHOw1BQ1uV|m(ksQuSW_V$M5 za*o#|HS1MNg(m*t@zX3nc6 zA(B?@JzssH`{WKZG>wo+((Y140ako2D9fPS7mlK2sg@pgi+M!JO$fJpdMExZlrbDJp|7NI zr@!+_3}B7pC<4NzHq#*RIm9qql(u+KRdI+mx2vPfTv1N8Rj$XlPa5RjYGNMqIaD@c zxutD>4UeZ%68%3Ja=)F>&{eVm&y5KxOqj$+lnnE^_DR^kV}!u3X~i}T4!~V@(EJMuET5X{Nsln5vz_r$gJ*De zLIxMz2WE+(*z|W&Kq5Pshu_*32P*2>aJpJ^kb_jnl8>c)F;4A^;8X8^y8O!TqjdCp z9^WONIZ-aHFNfwlXlZHb>ZT_o^zpcWPD3%1iG1`+?7#)(Z1eKm#DJCkl?bS+vP)|1 zlwqk>l2Nv7Bru><%UR*@atG__(G3?Aud^?~_*~%(7_t$c+I#vvSN_2--$L)F{@ z)MW6qyqr3utSXEoYFoCxFH3oyw+le&_IpDrGNQ&37yjqDrS`>;yzPJ6UXNQBH=2ycZTM=NVG#aa zu$>3ig-xC)^9d3)i!X(jB$tl=p%~ehUSmXC{bg}sLs<86Hb=1o5{otm)zg0`tXl*a z=tQ;CLWyfcNTr9P_1@`(daJIfPeHf2&|*SkiBeav(dF^SAl^;p*~}>;E$zaI0CQMj z#LQB?4IPNPakQePIMg+c-I77SsQrheQ1sf|_8!* zxogznmDAME1}*2f4q8ni?=q;dzOe0kL7osaPRnEw!L~lj+G@;3i*%lEm5s{E%ivCj z&f3gWEfMYKnk)1(?ybB{4c!hRy&ivBowYh8>5^`Mbe;yT(dHgJsI-8br#SXUFxaT3 z#EPCRl@lki32iT47bcLEXp4hpZ0Q8)gi3Ymxi;SSbMXK4%hEXPO=yDS=8l3pXION^ zNE{yxzT2ngTAnrtbu5cP58MKpPt8Wxv3XF4w6*T_!0PnTi}y1?CZx@3t>slH=F1D{6HnruA4W1C3T-DK6Tp8bU)2p zIGmbNl@+)D=*(QDL9YgMb9(s3=m#G7?Ifkyv8SbdFON|-IK()%SEa6Cc7|`y*|0(^s-*GD0nzC5)`X*tyWQ^?A2Z5`JiB^2#{`Gva$W$KF7didu;t;Edl3E=0qQw_%+7cpd5$79iX|>CH_3p)_vzGv zAPMJl5{ug%oNJo>n+p1Y$Ct1uBDdCycquCF#_eGoZ^)L>^+OvFd+c?0CKwOodzwZc zAeuh_c{Y+W9{)aeCZJocApR5YBc**L&6KVH$-kD+fqub8oIb6NV0AKY!4FlP@{*K& zb4BszNzkPz$o+P8ICC~}hr#W-9aq9r_M>Oe%iS&e>%5SnLId~5M?H^p*DYPgFpFx7 z-evduJ}X;(K4xmu?WWj<(k104Pc)M}w1YWz*VWnRITR}*PBpF8?&u_AQ#nLKj97aF z=W-fTi^Q#eW2qzxJw6yQ zVMpL6XtkIe_@g|$K0p3)YN4T>ZueXX1?|bg{E3XLS(k*)oG!$Xe@~XM{{&dCr3@@A zEDQ+?<4Xvbi$9;?bB>h@4{@Ze*x`BSfRM;?bJuGn^q_%tWK-iw_I5s*CzO=c{*1E z;G^6&wAZq2JRTRl6l$-xXH080$jbmsfd;|ljJ|hLQRj^xbUnNuCMPEk4-ac=*@wCQ zv2^nh0Uqpqg|(6Td{dt8`X)}z)+{V8!^riI&(>Fh&*?MGhZ~gl)9scn!%;*V_SQPk z_3+=>03j9!Zit;rF*UXTH`1L$I>CIa#W#QK%2p@=9%A00U{u?WU=m? z2~^(hCVjcwOZOCx3A}dpZDwN&_kElvjK>!6R8g>Vw7gpfcl0NAxX6QY^x98%<^-4b zzWKk$jeT{y>USfhy*tlXLx_^6(X6g)oXC_eGShK;b{DhaYU4Ud=STl7=zRk|+Z=0p z9R*WbKD^xQVaKyOkZk+a;^(iX5J~5{?{z$G<9U|XxQU23Pn!4=@b29#?7ch}kz|RJ zBVmtvc*@+QLtbe5LV^~MtQGtp_gh;J*N0er zo&x!S+L-YM9|&;LIg#Ja-FRzMU*CIcNj~S{W~lM}dl7g{;IVr?6?BSE49d@j z^F3Le89N$>&nhqk=%|wv!EB_{-(0ZE9()=uCR_4zC2>Q{x8oCS8n>F%@?xnd=4BwT zH!Uw|S;^w!xlsxZKI)AF9JCAjR*O}6^n0h@agw31Nl+2CTTJj^rMVnftt!3xl~GlX zZ55Yvh)o_if*XxaW~1BYxG!$h`rU>!vX9STCYPc3K83F3MbV5-WP@Yudq;v8uW?Ll8ey zui3Xs5QPtQGeB)PfDiTvkBEKvaq5%o#6zg_Zr6HG7?~*8Nm7r-vP*vMdg95$vGAvH z&hyud_z96mk&+%%K1y1TWAQ&`z*{#5^_MhO3);Sq(4oM#56z?Fe(nh;K(91R$wid#5NXyQJa5U1Osu1f-y_x}FYdDDh6~Y^ zYT*;DUglxOxvYJJJb`wpYF=7Y&H5VOXskl>T|$h3c9j;sN+8tGpQ&cdmANIh2d|^c z6+qYUmZQjbVzy5Omp3hrLOx@E@-pwzS_<@VjL9dHCUy~-hfegNQ7s`+mFiR|PB`{u z&{ZBjbg%25xhMa~Lr+i7)|OEL4&8pF07x0&6pmmeYRko?4JjeV-^qEdT&+Eem&(9D zPc)O_Rov89`k%GNtgZH6oqaG4^?Z$&Nr%etMh2igVg5vsI(k1ob6GqY;b{8lj~?p` zm<{D$f0#f*f#vA&SR)&xy##&(>O=YKIS@uOjcR_mEee;hfuhFz+a(^9ltZkeZvnzw zKRD!|mhpeLp+A2u@XR4)%=hkafIERueJB71_litRuEk;u1u*afH~tF1sI Date: Thu, 17 Sep 2020 20:06:54 +0200 Subject: [PATCH 10/17] minimal Markdown syntax fixes and lang tweaks to READMEs --- cloud-operations/README.md | 2 +- .../README.md | 25 ++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/cloud-operations/README.md b/cloud-operations/README.md index dcc30765f..afe4f15c1 100644 --- a/cloud-operations/README.md +++ b/cloud-operations/README.md @@ -12,7 +12,7 @@ The example's feed tracks changes to Google Compute instances, and the Cloud Fun ## Scheduled Cloud Asset Inventory Export to Bigquery - This [example](./scheduled-asset-inventory-export-bq) shows how to leverage [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature to keep track of your organization wide assets over time storing information in Bigquery. Data stored in Bigquery can then be used for different purposes, for example: dashboarding, analysis. + This [example](./scheduled-asset-inventory-export-bq) shows how to leverage the [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature, to keep track of your organization's assets over time storing information in Bigquery. Data stored in Bigquery can then be used for different purposes like dashboarding or analysis.
diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 149453d16..a828b6238 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -1,24 +1,20 @@ # Scheduled Cloud Asset Inventory Export to Bigquery -This example shows how to leverage [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature to keep track of your organization wide assets over time storing information in Bigquery. +This example shows how to leverage the [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature, to keep track of your organization's assets over time storing information in Bigquery. The data stored in Bigquery can then be used for different purposes like dashboarding or analysis. -The data stored in Bigquery can then be used for different purposes: - -- dashboarding -- analysis - -This example shows an export to Bigquery scheduled on a daily basis. - -The resources created in this example are shown in the high level diagram below: +This example shows an export to Bigquery scheduled on a daily basis. The resources created in this example are shown in the high level diagram below: ## Prerequisites -Ensure that you grant your account one of the following roles on your project, folder, or organization. - - Cloud Asset Viewer role (roles/cloudasset.viewer) - - Owner primitive role (roles/owner) + +Ensure that you grant your account one of the following roles on your project, folder, or organization: + +- Cloud Asset Viewer role (`roles/cloudasset.viewer`) +- Owner primitive role (`roles/owner`) ## Running the example + Clone this repository, specify your variables in a `terraform.tvars` and then go through the following steps to create resources: - `terraform init` @@ -28,9 +24,10 @@ Once done testing, you can clean up resources by running `terraform destroy`. To ## Testing the example -You can now run queries on the data you exported on Bigquery. [Here](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery#querying_an_asset_snapshot) you can find some example of queries you can run. +Once resources are created, you can run queries on the data you exported on Bigquery. [Here](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery#querying_an_asset_snapshot) you can find some example of queries you can run. + +You can also create a dashboard connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase. -You can also create a dashborad connecting [Datalab](https://datastudio.google.com/) or any other BI tools of your choice to your Bigquery datase.. ## Variables From 8464fbacc43b3d1f4e4f3de938f85cb003c73d61 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 24 Sep 2020 17:16:43 +0200 Subject: [PATCH 11/17] Fix roles Implement PR comments Make example at Project level to keep it simpler --- .../README.md | 13 +- .../cf/main.py | 115 ++++++++++-------- .../main.tf | 33 +++-- .../outputs.tf | 16 +-- .../variables.tf | 32 +++-- .../versions.tf | 17 +++ 6 files changed, 138 insertions(+), 88 deletions(-) create mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/versions.tf diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 149453d16..be27eb713 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -1,13 +1,16 @@ # Scheduled Cloud Asset Inventory Export to Bigquery -This example shows how to leverage [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature to keep track of your organization wide assets over time storing information in Bigquery. +This example shows how to leverage [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature to keep track of your project wide assets over time storing information in Bigquery. The data stored in Bigquery can then be used for different purposes: - dashboarding - analysis -This example shows an export to Bigquery scheduled on a daily basis. +The example uses export resources at project level for ease of testing, in actual use a few changes are needed to operate at the resource hierarchy level: + + - the export should be set at the folder or organization level + - the `roles/cloudasset.viewer` on the service account should be set at folder or organization level The resources created in this example are shown in the high level diagram below: @@ -36,8 +39,10 @@ You can also create a dashborad connecting [Datalab](https://datastudio.google.c | name | description | type | required | default | |---|---|:---: |:---:|:---:| -| cai_config | Cloud Asset inventory export config. | object({...}) | ✓ | | +| billing_account | Billing account id used as default for new projects. | string | ✓ | | +| cai_config | Cloud Asset inventory export config. | object({...}) | ✓ | | | project_id | Project id that references existing project. | string | ✓ | | +| root_node | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | | *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | | *name* | Arbitrary string used to name created resources. | string | | asset-inventory | | *project_create* | Create project instead ofusing an existing one. | bool | | false | @@ -48,5 +53,5 @@ You can also create a dashborad connecting [Datalab](https://datastudio.google.c | name | description | sensitive | |---|---|:---:| | bq-dataset | Bigquery instance details. | | -| cloud-function | Bigquery instance details. | | +| cloud-function | Cloud Function instance details. | | diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py index 63a964d5a..30927a95d 100755 --- a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py @@ -34,73 +34,86 @@ from google.cloud import asset_v1 import googleapiclient.discovery import googleapiclient.errors + def _configure_logging(verbose=True): - """Basic logging configuration. - Args: - verbose: enable verbose logging - """ - level = logging.DEBUG if verbose else logging.INFO - logging.basicConfig(level=level) - warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) + """Basic logging configuration. + Args: + verbose: enable verbose logging + """ + level = logging.DEBUG if verbose else logging.INFO + logging.basicConfig(level=level) + warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) + @click.command() -@click.option('--organization', required=True, - help='Organization ID') +@click.option('--project', required=True, + help='Project ID') @click.option('--bq-project', required=True, help='Bigquery project to use.') @click.option('--bq-dataset', required=True, help='Bigquery dataset to use.') @click.option('--bq-table', required=True, help='Bigquery table name to use.') -@click.option('--read-time', required=True, - help='Day to take an asset snapshot in the format \'YYYYMMDD\'. \ - If not specified run for the current day. Export will run for \ - the midnight of the specified day.') +@click.option('--read-time', required=False, + help=('Day to take an asset snapshot in the format \'YYYYMMDD\'.' + 'If not specified run for the current day. Export will' + 'run for the midnight of the specified day.')) @click.option('--verbose', is_flag=True, help='Verbose output') -def main_cli(organization=None, bq_project=None, bq_dataset=None, bq_table=None, read_time=None, verbose=False): - """Trigger Cloud Asset inventory export to Bigquery. Data will - be stored in the dataset specified on a dated table with the name - specified. - """ - try: - _main(organization, bq_project, bq_dataset, bq_table) - except RuntimeError: - logging.exception('exception raised') +def main_cli(project=None, bq_project=None, bq_dataset=None, + bq_table=None, read_time=None, verbose=False): + """Trigger Cloud Asset inventory export to Bigquery. Data will + be stored in the dataset specified on a dated table with the name + specified. + """ + try: + _main(project, bq_project, bq_dataset, + bq_table, read_time, verbose) + except RuntimeError: + logging.exception('exception raised') def main(event, context): - """Cloud Function entry point.""" - try: - _main(**event) - # uncomment once https://issuetracker.google.com/issues/155215191 is fixed - # except RuntimeError: - # raise - except Exception: - logging.exception('exception in cloud function entry point') + """Cloud Function entry point.""" + try: + data = json.loads(base64.b64decode(event['data']).decode('utf-8')) + print(data) + _main(**data) + # uncomment once https://issuetracker.google.com/issues/155215191 is fixed + # except RuntimeError: + # raise + except Exception: + logging.exception('exception in cloud function entry point') -def _main(organization=None, bq_project=None, bq_dataset=None, bq_table=None, read_time=None, verbose=False): - """Module entry point used by cli and cloud function wrappers.""" +def _main(project=None, bq_project=None, bq_dataset=None, bq_table=None, read_time=None, verbose=False): + """Module entry point used by cli and cloud function wrappers.""" - if not read_time: - read_time = datetime.date.today().strftime("%Y%m%d") - - client = asset_v1.AssetServiceClient() - parent = "organizations/%s" % organization - content_type = asset_v1.ContentType.RESOURCE - output_config = asset_v1.OutputConfig() - output_config.bigquery_destination.dataset = "projects/%s/datasets/%s" % (bq_project, bq_dataset) - output_config.bigquery_destination.table = "%s_%s" % (bq_table, read_time) - output_config.bigquery_destination.force = True - response = client.export_assets( - request={ - "parent": parent, - "read_time": datetime.datetime.strptime(read_time, '%Y%m%d'), - "content_type": content_type, - "output_config": output_config - } - ) + _configure_logging(verbose) + if not read_time: + read_time = datetime.datetime.now() + client = asset_v1.AssetServiceClient() + parent = 'projects/%s' % project + content_type = asset_v1.ContentType.RESOURCE + output_config = asset_v1.OutputConfig() + output_config.bigquery_destination.dataset = 'projects/%s/datasets/%s' % ( + bq_project, bq_dataset) + output_config.bigquery_destination.table = '%s_%s' % ( + bq_table, read_time.strftime('%Y%m%d')) + output_config.bigquery_destination.force = True + try: + response = client.export_assets( + request={ + 'parent': parent, + 'read_time': read_time, + 'content_type': content_type, + 'output_config': output_config + } + ) + except (GoogleAPIError, googleapiclient.errors.HttpError) as e: + logging.debug('API Error: %s', e, exc_info=True) + raise RuntimeError( + 'Error fetching Asset Inventory entries (project: %s)' % parent, e) if __name__ == '__main__': - main_cli() + main_cli() diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 3fd065024..150dcea25 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -20,8 +20,8 @@ module "project" { source = "../../modules/project" name = var.project_id - parent = "folders/572946148602" - billing_account = "0074F6-6FDF57-1B2DD0" + parent = var.root_node + billing_account = var.billing_account project_create = var.project_create services = [ "bigquery.googleapis.com", @@ -39,9 +39,8 @@ module "service-account" { project_id = module.project.project_id names = ["${var.name}-cf"] iam_project_roles = { - (module.project.name) = [ - "roles/cloudasset.viewer", - "roles/cloudfunctions.invoker" + (var.project_id) = [ + "roles/cloudasset.viewer" ] } } @@ -77,10 +76,6 @@ module "cf" { output_path = var.bundle_path } service_account = module.service-account.email - iam_roles = ["roles/cloudfunctions.invoker"] - iam_members = { - "roles/cloudfunctions.invoker" = ["serviceAccount:${module.service-account.email}"] - } trigger_config = { event = "google.pubsub.topic.publish" resource = module.pubsub.topic.id @@ -97,26 +92,25 @@ resource "random_pet" "random" { ############################################################################### resource "google_app_engine_application" "app" { project = module.project.project_id - location_id = "europe-west" + location_id = var.location } resource "google_cloud_scheduler_job" "job" { - project = module.project.project_id + project = google_app_engine_application.app.project region = var.region name = "test-job" description = "test http job" schedule = "* 9 * * 1" time_zone = "Etc/UTC" - attempt_deadline = "320s" pubsub_target { attributes = {} topic_name = module.pubsub.topic.id data = base64encode(jsonencode({ - organization = var.cai_config.organization - bq_project = var.project_id - bq_dataset = var.cai_config.bq_dataset - bq_table = var.cai_config.bq_table + project = module.project.project_id + bq_project = module.project.project_id + bq_dataset = var.cai_config.bq_dataset + bq_table = var.cai_config.bq_table })) } } @@ -124,7 +118,7 @@ resource "google_cloud_scheduler_job" "job" { ############################################################################### # Bigquery # ############################################################################### -module "bigquery-dataset" { +module "bq" { source = "../../modules/bigquery-dataset" project_id = module.project.project_id id = var.cai_config.bq_dataset @@ -134,4 +128,9 @@ module "bigquery-dataset" { access_identities = { owner = module.service-account.email } + options = { + default_table_expiration_ms = null + default_partition_expiration_ms = null + delete_contents_on_destroy = true + } } diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf index e4ad633b1..efee4737d 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf @@ -14,12 +14,12 @@ * limitations under the License. */ -output "bq-dataset" { - description = "Bigquery instance details." - value = module.bq.dataset -} +# output "bq-dataset" { +# description = "Bigquery instance details." +# value = module.bq.dataset +# } -output "cloud-function" { - description = "Bigquery instance details." - value = module.bq.dataset -} +# output "cloud-function" { +# description = "Cloud Function instance details." +# value = module.cf.function +# } diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf index 85311ee1e..80e5be855 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/variables.tf @@ -14,12 +14,32 @@ * limitations under the License. */ +variable "billing_account" { + description = "Billing account id used as default for new projects." + type = string +} + variable "bundle_path" { description = "Path used to write the intermediate Cloud Function code bundle." type = string default = "./bundle.zip" } +variable "cai_config" { + description = "Cloud Asset inventory export config." + type = object({ + bq_dataset = string + bq_table = string + }) +} + +variable "location" { + description = "Appe Engine location used in the example." + type = string + default = "europe-west" +} + + variable "name" { description = "Arbitrary string used to name created resources." type = string @@ -29,7 +49,7 @@ variable "name" { variable "project_create" { description = "Create project instead ofusing an existing one." type = bool - default = false + default = true } variable "project_id" { @@ -43,11 +63,7 @@ variable "region" { default = "europe-west1" } -variable "cai_config" { - description = "Cloud Asset inventory export config." - type = object({ - organization = string - bq_dataset = string - bq_table = string - }) +variable "root_node" { + description = "The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id." + type = string } diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf b/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf new file mode 100644 index 000000000..057095c0f --- /dev/null +++ b/cloud-operations/scheduled-asset-inventory-export-bq/versions.tf @@ -0,0 +1,17 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +terraform { + required_version = ">= 0.12.6" +} From 68115ee4faff5a27e9598d0b96ade9452f4119f7 Mon Sep 17 00:00:00 2001 From: Lorenzo Caggioni Date: Thu, 24 Sep 2020 17:32:26 +0200 Subject: [PATCH 12/17] Remove cloud shell file --- .../cloud-shell-readme.txt | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt b/cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt deleted file mode 100644 index e799e3e99..000000000 --- a/cloud-operations/scheduled-asset-inventory-export-bq/cloud-shell-readme.txt +++ /dev/null @@ -1,13 +0,0 @@ - - -################################# Quickstart ################################# - -- terraform init -- terraform apply -var project_id=$GOOGLE_CLOUD_PROJECT \ - -var --organization=ORGANIZATION_ID \ - -var --bq-table=TABLE_NAME \ - -var --bq-project=PROJECT_ID \ - -var --bq-dataset=DATASET_NAME - -Refer to the README.md file for more info. - From c192fd2bafebadafb1516dd5938de81e748e5842 Mon Sep 17 00:00:00 2001 From: lcaggio Date: Thu, 24 Sep 2020 17:40:51 +0200 Subject: [PATCH 13/17] Update outputs.tf fix output --- .../outputs.tf | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf index efee4737d..fa07eea03 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/outputs.tf @@ -14,12 +14,12 @@ * limitations under the License. */ -# output "bq-dataset" { -# description = "Bigquery instance details." -# value = module.bq.dataset -# } +output "bq-dataset" { + description = "Bigquery instance details." + value = module.bq.dataset +} -# output "cloud-function" { -# description = "Cloud Function instance details." -# value = module.cf.function -# } +output "cloud-function" { + description = "Cloud Function instance details." + value = module.cf.function +} From 290ddd56ab6549faf7702793829f5d6adcc7f508 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 24 Sep 2020 18:48:25 +0200 Subject: [PATCH 14/17] use consistent quotes, 2 spaces per tab --- .../cf/main.py | 127 +++++++++--------- 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py index 30927a95d..80d629f33 100755 --- a/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py +++ b/cloud-operations/scheduled-asset-inventory-export-bq/cf/main.py @@ -36,84 +36,77 @@ import googleapiclient.errors def _configure_logging(verbose=True): - """Basic logging configuration. - Args: - verbose: enable verbose logging - """ - level = logging.DEBUG if verbose else logging.INFO - logging.basicConfig(level=level) - warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) + '''Basic logging configuration. + Args: + verbose: enable verbose logging + ''' + level = logging.DEBUG if verbose else logging.INFO + logging.basicConfig(level=level) + warnings.filterwarnings('ignore', r'.*end user credentials.*', UserWarning) @click.command() -@click.option('--project', required=True, - help='Project ID') -@click.option('--bq-project', required=True, - help='Bigquery project to use.') -@click.option('--bq-dataset', required=True, - help='Bigquery dataset to use.') -@click.option('--bq-table', required=True, - help='Bigquery table name to use.') -@click.option('--read-time', required=False, - help=('Day to take an asset snapshot in the format \'YYYYMMDD\'.' - 'If not specified run for the current day. Export will' - 'run for the midnight of the specified day.')) +@click.option('--project', required=True, help='Project ID') +@click.option('--bq-project', required=True, help='Bigquery project to use.') +@click.option('--bq-dataset', required=True, help='Bigquery dataset to use.') +@click.option('--bq-table', required=True, help='Bigquery table name to use.') +@click.option('--read-time', required=False, help=( + 'Day to take an asset snapshot in \'YYYYMMDD\' format, uses current day ' + ' as default. Export will run at midnight of the specified day.')) @click.option('--verbose', is_flag=True, help='Verbose output') -def main_cli(project=None, bq_project=None, bq_dataset=None, - bq_table=None, read_time=None, verbose=False): - """Trigger Cloud Asset inventory export to Bigquery. Data will - be stored in the dataset specified on a dated table with the name - specified. - """ - try: - _main(project, bq_project, bq_dataset, - bq_table, read_time, verbose) - except RuntimeError: - logging.exception('exception raised') +def main_cli(project=None, bq_project=None, bq_dataset=None, bq_table=None, + read_time=None, verbose=False): + '''Trigger Cloud Asset inventory export to Bigquery. Data will be stored in + the dataset specified on a dated table with the name specified. + ''' + try: + _main(project, bq_project, bq_dataset, bq_table, read_time, verbose) + except RuntimeError: + logging.exception('exception raised') def main(event, context): - """Cloud Function entry point.""" - try: - data = json.loads(base64.b64decode(event['data']).decode('utf-8')) - print(data) - _main(**data) - # uncomment once https://issuetracker.google.com/issues/155215191 is fixed - # except RuntimeError: - # raise - except Exception: - logging.exception('exception in cloud function entry point') + 'Cloud Function entry point.' + try: + data = json.loads(base64.b64decode(event['data']).decode('utf-8')) + print(data) + _main(**data) + # uncomment once https://issuetracker.google.com/issues/155215191 is fixed + # except RuntimeError: + # raise + except Exception: + logging.exception('exception in cloud function entry point') def _main(project=None, bq_project=None, bq_dataset=None, bq_table=None, read_time=None, verbose=False): - """Module entry point used by cli and cloud function wrappers.""" + 'Module entry point used by cli and cloud function wrappers.' - _configure_logging(verbose) - if not read_time: - read_time = datetime.datetime.now() - client = asset_v1.AssetServiceClient() - parent = 'projects/%s' % project - content_type = asset_v1.ContentType.RESOURCE - output_config = asset_v1.OutputConfig() - output_config.bigquery_destination.dataset = 'projects/%s/datasets/%s' % ( - bq_project, bq_dataset) - output_config.bigquery_destination.table = '%s_%s' % ( - bq_table, read_time.strftime('%Y%m%d')) - output_config.bigquery_destination.force = True - try: - response = client.export_assets( - request={ - 'parent': parent, - 'read_time': read_time, - 'content_type': content_type, - 'output_config': output_config - } - ) - except (GoogleAPIError, googleapiclient.errors.HttpError) as e: - logging.debug('API Error: %s', e, exc_info=True) - raise RuntimeError( - 'Error fetching Asset Inventory entries (project: %s)' % parent, e) + _configure_logging(verbose) + if not read_time: + read_time = datetime.datetime.now() + client = asset_v1.AssetServiceClient() + parent = 'projects/%s' % project + content_type = asset_v1.ContentType.RESOURCE + output_config = asset_v1.OutputConfig() + output_config.bigquery_destination.dataset = 'projects/%s/datasets/%s' % ( + bq_project, bq_dataset) + output_config.bigquery_destination.table = '%s_%s' % ( + bq_table, read_time.strftime('%Y%m%d')) + output_config.bigquery_destination.force = True + try: + response = client.export_assets( + request={ + 'parent': parent, + 'read_time': read_time, + 'content_type': content_type, + 'output_config': output_config + } + ) + except (GoogleAPIError, googleapiclient.errors.HttpError) as e: + logging.debug('API Error: %s', e, exc_info=True) + raise RuntimeError( + 'Error fetching Asset Inventory entries (project: %s)' % parent, e) if __name__ == '__main__': - main_cli() + main_cli() From 0cbc6556e0d754f0b8c797b511a03cb887142744 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 24 Sep 2020 18:48:40 +0200 Subject: [PATCH 15/17] remove merge diff --- .../scheduled-asset-inventory-export-bq/README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index 9c63f2b48..ea60ef0a4 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -1,6 +1,5 @@ # Scheduled Cloud Asset Inventory Export to Bigquery -<<<<<<< HEAD This example shows how to leverage [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature to keep track of your project wide assets over time storing information in Bigquery. The data stored in Bigquery can then be used for different purposes: @@ -8,17 +7,12 @@ The data stored in Bigquery can then be used for different purposes: - dashboarding - analysis -The example uses export resources at project level for ease of testing, in actual use a few changes are needed to operate at the resource hierarchy level: +The example uses export resources at the project level for ease of testing, in actual use a few changes are needed to operate at the resource hierarchy level: - - the export should be set at the folder or organization level - - the `roles/cloudasset.viewer` on the service account should be set at folder or organization level +- the export should be set at the folder or organization level +- the `roles/cloudasset.viewer` on the service account should be set at the folder or organization level The resources created in this example are shown in the high level diagram below: -======= -This example shows how to leverage the [Cloud Asset Inventory Exporting to Bigquery](https://cloud.google.com/asset-inventory/docs/exporting-to-bigquery) feature, to keep track of your organization's assets over time storing information in Bigquery. The data stored in Bigquery can then be used for different purposes like dashboarding or analysis. - -This example shows an export to Bigquery scheduled on a daily basis. The resources created in this example are shown in the high level diagram below: ->>>>>>> a2392aeda1c0a3da04b80666d3eb4b09061eeb04 From 703bca7c0b9a4d9d7708a2245f9b32f64d82d388 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 24 Sep 2020 18:49:16 +0200 Subject: [PATCH 16/17] reformat --- .../scheduled-asset-inventory-export-bq/main.tf | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf index 150dcea25..f1f07aea7 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/main.tf +++ b/cloud-operations/scheduled-asset-inventory-export-bq/main.tf @@ -39,9 +39,7 @@ module "service-account" { project_id = module.project.project_id names = ["${var.name}-cf"] iam_project_roles = { - (var.project_id) = [ - "roles/cloudasset.viewer" - ] + (var.project_id) = ["roles/cloudasset.viewer"] } } @@ -96,12 +94,12 @@ resource "google_app_engine_application" "app" { } resource "google_cloud_scheduler_job" "job" { - project = google_app_engine_application.app.project - region = var.region - name = "test-job" - description = "test http job" - schedule = "* 9 * * 1" - time_zone = "Etc/UTC" + project = google_app_engine_application.app.project + region = var.region + name = "test-job" + description = "test http job" + schedule = "* 9 * * 1" + time_zone = "Etc/UTC" pubsub_target { attributes = {} From d18dcea9d2a835591b5a33aed6036d5abdb83fd7 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 24 Sep 2020 18:49:48 +0200 Subject: [PATCH 17/17] update README in/out table --- cloud-operations/scheduled-asset-inventory-export-bq/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloud-operations/scheduled-asset-inventory-export-bq/README.md b/cloud-operations/scheduled-asset-inventory-export-bq/README.md index ea60ef0a4..40fe75a5c 100644 --- a/cloud-operations/scheduled-asset-inventory-export-bq/README.md +++ b/cloud-operations/scheduled-asset-inventory-export-bq/README.md @@ -48,8 +48,9 @@ You can also create a dashboard connecting [Datalab](https://datastudio.google.c | project_id | Project id that references existing project. | string | ✓ | | | root_node | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | | *bundle_path* | Path used to write the intermediate Cloud Function code bundle. | string | | ./bundle.zip | +| *location* | Appe Engine location used in the example. | string | | europe-west | | *name* | Arbitrary string used to name created resources. | string | | asset-inventory | -| *project_create* | Create project instead ofusing an existing one. | bool | | false | +| *project_create* | Create project instead ofusing an existing one. | bool | | true | | *region* | Compute region used in the example. | string | | europe-west1 | ## Outputs