Skip to content

Commit dcc51f7

Browse files
committed
feat: Add flag for import existing resources
Pass through option for importing existing resources which allows using Retain for DeletionPolicy on resources to avoid removal on data resources if stack needs to be deleted and recreated. Fixes: #1699
1 parent 1efd569 commit dcc51f7

File tree

11 files changed

+99
-6
lines changed

11 files changed

+99
-6
lines changed

samcli/commands/deploy/command.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@
108108
is_flag=True,
109109
help="Prompt to confirm if the computed changeset is to be deployed by SAM CLI.",
110110
)
111+
@click.option(
112+
"--import-existing-resources/--no-import-existing-resources",
113+
default=False,
114+
required=False,
115+
is_flag=True,
116+
help="Requests CloudFormation to import any existing referenced by the template when creating the changeset.",
117+
)
111118
@click.option(
112119
"--disable-rollback/--no-disable-rollback",
113120
default=False,
@@ -198,6 +205,7 @@ def cli(
198205
config_file,
199206
config_env,
200207
disable_rollback,
208+
import_existing_resources,
201209
on_failure,
202210
max_wait_duration,
203211
):
@@ -234,6 +242,7 @@ def cli(
234242
config_env,
235243
resolve_image_repos,
236244
disable_rollback,
245+
import_existing_resources,
237246
on_failure,
238247
max_wait_duration,
239248
) # pragma: no cover
@@ -268,6 +277,7 @@ def do_cli(
268277
config_env,
269278
resolve_image_repos,
270279
disable_rollback,
280+
import_existing_resources,
271281
on_failure,
272282
max_wait_duration,
273283
):
@@ -373,6 +383,7 @@ def do_cli(
373383
signing_profiles=guided_context.signing_profiles if guided else signing_profiles,
374384
use_changeset=True,
375385
disable_rollback=guided_context.disable_rollback if guided else disable_rollback,
386+
import_existing_resources=import_existing_resources,
376387
poll_delay=poll_delay,
377388
on_failure=on_failure,
378389
max_wait_duration=max_wait_duration,

samcli/commands/deploy/core/options.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"fail_on_empty_changeset",
3636
"confirm_changeset",
3737
"disable_rollback",
38+
"import_existing_resources",
3839
"on_failure",
3940
"force_upload",
4041
"max_wait_duration",

samcli/commands/deploy/deploy_context.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def __init__(
7272
signing_profiles,
7373
use_changeset,
7474
disable_rollback,
75+
import_existing_resources,
7576
poll_delay,
7677
on_failure,
7778
max_wait_duration,
@@ -104,6 +105,7 @@ def __init__(
104105
self.signing_profiles = signing_profiles
105106
self.use_changeset = use_changeset
106107
self.disable_rollback = disable_rollback
108+
self.import_existing_resources = import_existing_resources
107109
self.poll_delay = poll_delay
108110
self.on_failure = FailureMode(on_failure) if on_failure else FailureMode.ROLLBACK
109111
self._max_template_size = 51200
@@ -164,6 +166,7 @@ def run(self):
164166
self.signing_profiles,
165167
self.use_changeset,
166168
self.disable_rollback,
169+
self.import_existing_resources,
167170
)
168171
return self.deploy(
169172
self.stack_name,
@@ -180,6 +183,7 @@ def run(self):
180183
self.confirm_changeset,
181184
self.use_changeset,
182185
self.disable_rollback,
186+
self.import_existing_resources,
183187
)
184188

185189
def deploy(
@@ -198,6 +202,7 @@ def deploy(
198202
confirm_changeset: bool = False,
199203
use_changeset: bool = True,
200204
disable_rollback: bool = False,
205+
import_existing_resources: bool = False,
201206
):
202207
"""
203208
Deploy the stack to cloudformation.
@@ -234,6 +239,8 @@ def deploy(
234239
Involve creation of changesets, false when using sam sync
235240
disable_rollback : bool
236241
Preserves the state of previously provisioned resources when an operation fails
242+
import_existing_resources : bool
243+
Changeset should attempt to import existing resources as supported by cloudformation
237244
"""
238245
stacks, _ = SamLocalStackProvider.get_stacks(
239246
self.template_file,
@@ -257,6 +264,7 @@ def deploy(
257264
notification_arns=notification_arns,
258265
s3_uploader=s3_uploader,
259266
tags=tags,
267+
import_existing_resources=import_existing_resources,
260268
)
261269
click.echo(self.MSG_SHOWCASE_CHANGESET.format(changeset_id=result["Id"]))
262270

samcli/commands/deploy/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def print_deploy_args(
2020
signing_profiles,
2121
use_changeset,
2222
disable_rollback,
23+
import_existing_resources,
2324
):
2425
"""
2526
Print a table of the values that are used during a sam deploy.
@@ -33,6 +34,7 @@ def print_deploy_args(
3334
Region : us-east-1
3435
Confirm changeset : False
3536
Disable rollback : False
37+
Import Existing Resources : False
3638
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-abcdef
3739
Capabilities : ["CAPABILITY_IAM"]
3840
Parameter overrides : {'MyParamater': '***', 'Parameter2': 'dd'}
@@ -70,6 +72,7 @@ def print_deploy_args(
7072
if use_changeset:
7173
click.echo(f"\tConfirm changeset : {confirm_changeset}")
7274
click.echo(f"\tDisable rollback : {disable_rollback}")
75+
click.echo(f"\tImport Existing Resources : {import_existing_resources}")
7376
if image_repository:
7477
msg = "Deployment image repository : "
7578
# NOTE(sriram-mv): tab length is 8 spaces.

samcli/commands/sync/command.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ def do_cli(
398398
force_upload=True,
399399
signing_profiles=None,
400400
disable_rollback=False,
401+
import_existing_resources=False,
401402
poll_delay=poll_delay,
402403
on_failure=None,
403404
max_wait_duration=60,

samcli/lib/deploy/deployer.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,16 @@ def has_stack(self, stack_name):
141141
raise e
142142

143143
def create_changeset(
144-
self, stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags
144+
self,
145+
stack_name,
146+
cfn_template,
147+
parameter_values,
148+
capabilities,
149+
role_arn,
150+
notification_arns,
151+
s3_uploader,
152+
tags,
153+
import_existing_resources,
145154
):
146155
"""
147156
Call Cloudformation to create a changeset and wait for it to complete
@@ -154,6 +163,7 @@ def create_changeset(
154163
:param notification_arns: Arns for sending notifications
155164
:param s3_uploader: S3Uploader object to upload files to S3 buckets
156165
:param tags: Array of tags passed to CloudFormation
166+
:param import_existing_resources: Argument passed to CloudFormation
157167
:return:
158168
"""
159169
if not self.has_stack(stack_name):
@@ -183,6 +193,7 @@ def create_changeset(
183193
"Parameters": parameter_values,
184194
"Description": "Created by SAM CLI at {0} UTC".format(datetime.utcnow().isoformat()),
185195
"Tags": tags,
196+
"ImportExistingResources": import_existing_resources,
186197
}
187198

188199
kwargs = self._process_kwargs(kwargs, s3_uploader, capabilities, role_arn, notification_arns)
@@ -245,8 +256,8 @@ def describe_changeset(self, change_set_id, stack_name, **kwargs):
245256
"""
246257
paginator = self._client.get_paginator("describe_change_set")
247258
response_iterator = paginator.paginate(ChangeSetName=change_set_id, StackName=stack_name)
248-
changes = {"Add": [], "Modify": [], "Remove": []}
249-
changes_showcase = {"Add": "+ Add", "Modify": "* Modify", "Remove": "- Delete"}
259+
changes = {"Add": [], "Modify": [], "Remove": [], "Import": []}
260+
changes_showcase = {"Add": "+ Add", "Modify": "* Modify", "Remove": "- Delete", "Import": "~ Import"}
250261
changeset = False
251262
for item in response_iterator:
252263
cf_changes = item.get("Changes")
@@ -556,11 +567,28 @@ def wait_for_execute(
556567
raise ex
557568

558569
def create_and_wait_for_changeset(
559-
self, stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags
570+
self,
571+
stack_name,
572+
cfn_template,
573+
parameter_values,
574+
capabilities,
575+
role_arn,
576+
notification_arns,
577+
s3_uploader,
578+
tags,
579+
import_existing_resources,
560580
):
561581
try:
562582
result, changeset_type = self.create_changeset(
563-
stack_name, cfn_template, parameter_values, capabilities, role_arn, notification_arns, s3_uploader, tags
583+
stack_name,
584+
cfn_template,
585+
parameter_values,
586+
capabilities,
587+
role_arn,
588+
notification_arns,
589+
s3_uploader,
590+
tags,
591+
import_existing_resources,
564592
)
565593
self.wait_for_changeset(result["Id"], stack_name)
566594
self.describe_changeset(result["Id"], stack_name)

tests/unit/commands/deploy/test_command.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def setUp(self):
5050
self.use_changeset = True
5151
self.resolve_image_repos = False
5252
self.disable_rollback = False
53+
self.import_existing_resources = False
5354
self.on_failure = None
5455
self.max_wait_duration = 480
5556
MOCK_SAM_CONFIG.reset_mock()
@@ -103,6 +104,7 @@ def test_all_args(self, mock_deploy_context, mock_deploy_click, mock_package_con
103104
config_file=self.config_file,
104105
resolve_image_repos=self.resolve_image_repos,
105106
disable_rollback=self.disable_rollback,
107+
import_existing_resources=self.import_existing_resources,
106108
on_failure=self.on_failure,
107109
max_wait_duration=self.max_wait_duration,
108110
)
@@ -130,6 +132,7 @@ def test_all_args(self, mock_deploy_context, mock_deploy_click, mock_package_con
130132
signing_profiles=self.signing_profiles,
131133
use_changeset=self.use_changeset,
132134
disable_rollback=self.disable_rollback,
135+
import_existing_resources=self.import_existing_resources,
133136
poll_delay=os.getenv("SAM_CLI_POLL_DELAY"),
134137
on_failure=self.on_failure,
135138
max_wait_duration=self.max_wait_duration,
@@ -221,6 +224,7 @@ def test_all_args_guided_no_to_authorization_confirmation_prompt(
221224
config_file=self.config_file,
222225
resolve_image_repos=self.resolve_image_repos,
223226
disable_rollback=self.disable_rollback,
227+
import_existing_resources=self.import_existing_resources,
224228
on_failure=self.on_failure,
225229
max_wait_duration=self.max_wait_duration,
226230
)
@@ -323,6 +327,7 @@ def test_all_args_guided_use_defaults(
323327
config_file=self.config_file,
324328
resolve_image_repos=self.resolve_image_repos,
325329
disable_rollback=self.disable_rollback,
330+
import_existing_resources=self.import_existing_resources,
326331
on_failure=self.on_failure,
327332
max_wait_duration=self.max_wait_duration,
328333
)
@@ -350,6 +355,7 @@ def test_all_args_guided_use_defaults(
350355
signing_profiles=self.signing_profiles,
351356
use_changeset=self.use_changeset,
352357
disable_rollback=True,
358+
import_existing_resources=False,
353359
poll_delay=5,
354360
on_failure=self.on_failure,
355361
max_wait_duration=self.max_wait_duration,
@@ -469,6 +475,7 @@ def test_all_args_guided(
469475
config_file=self.config_file,
470476
resolve_image_repos=self.resolve_image_repos,
471477
disable_rollback=self.disable_rollback,
478+
import_existing_resources=self.import_existing_resources,
472479
on_failure=self.on_failure,
473480
max_wait_duration=self.max_wait_duration,
474481
)
@@ -496,6 +503,7 @@ def test_all_args_guided(
496503
signing_profiles=self.signing_profiles,
497504
use_changeset=self.use_changeset,
498505
disable_rollback=True,
506+
import_existing_resources=False,
499507
poll_delay=5,
500508
on_failure=self.on_failure,
501509
max_wait_duration=self.max_wait_duration,
@@ -618,6 +626,7 @@ def test_all_args_guided_no_save_echo_param_to_config(
618626
config_file=self.config_file,
619627
resolve_image_repos=self.resolve_image_repos,
620628
disable_rollback=self.disable_rollback,
629+
import_existing_resources=self.import_existing_resources,
621630
on_failure=self.on_failure,
622631
max_wait_duration=self.max_wait_duration,
623632
)
@@ -649,6 +658,7 @@ def test_all_args_guided_no_save_echo_param_to_config(
649658
signing_profiles=self.signing_profiles,
650659
use_changeset=self.use_changeset,
651660
disable_rollback=True,
661+
import_existing_resources=False,
652662
poll_delay=5,
653663
on_failure=self.on_failure,
654664
max_wait_duration=self.max_wait_duration,
@@ -778,6 +788,7 @@ def test_all_args_guided_no_params_save_config(
778788
signing_profiles=self.signing_profiles,
779789
resolve_image_repos=self.resolve_image_repos,
780790
disable_rollback=self.disable_rollback,
791+
import_existing_resources=self.import_existing_resources,
781792
on_failure=self.on_failure,
782793
max_wait_duration=self.max_wait_duration,
783794
)
@@ -805,6 +816,7 @@ def test_all_args_guided_no_params_save_config(
805816
signing_profiles=self.signing_profiles,
806817
use_changeset=self.use_changeset,
807818
disable_rollback=True,
819+
import_existing_resources=False,
808820
poll_delay=5,
809821
on_failure=self.on_failure,
810822
max_wait_duration=self.max_wait_duration,
@@ -918,6 +930,7 @@ def test_all_args_guided_no_params_no_save_config(
918930
signing_profiles=self.signing_profiles,
919931
resolve_image_repos=self.resolve_image_repos,
920932
disable_rollback=self.disable_rollback,
933+
import_existing_resources=self.import_existing_resources,
921934
on_failure=self.on_failure,
922935
max_wait_duration=self.max_wait_duration,
923936
)
@@ -945,6 +958,7 @@ def test_all_args_guided_no_params_no_save_config(
945958
signing_profiles=self.signing_profiles,
946959
use_changeset=self.use_changeset,
947960
disable_rollback=self.disable_rollback,
961+
import_existing_resources=self.import_existing_resources,
948962
poll_delay=5,
949963
on_failure=self.on_failure,
950964
max_wait_duration=self.max_wait_duration,
@@ -996,6 +1010,7 @@ def test_all_args_resolve_s3(
9961010
signing_profiles=self.signing_profiles,
9971011
resolve_image_repos=self.resolve_image_repos,
9981012
disable_rollback=self.disable_rollback,
1013+
import_existing_resources=self.import_existing_resources,
9991014
on_failure=self.on_failure,
10001015
max_wait_duration=self.max_wait_duration,
10011016
)
@@ -1023,6 +1038,7 @@ def test_all_args_resolve_s3(
10231038
signing_profiles=self.signing_profiles,
10241039
use_changeset=self.use_changeset,
10251040
disable_rollback=self.disable_rollback,
1041+
import_existing_resources=self.import_existing_resources,
10261042
poll_delay=5,
10271043
on_failure=self.on_failure,
10281044
max_wait_duration=self.max_wait_duration,
@@ -1062,6 +1078,7 @@ def test_resolve_s3_and_s3_bucket_both_set(self):
10621078
signing_profiles=self.signing_profiles,
10631079
resolve_image_repos=self.resolve_image_repos,
10641080
disable_rollback=self.disable_rollback,
1081+
import_existing_resources=self.import_existing_resources,
10651082
on_failure=self.on_failure,
10661083
max_wait_duration=self.max_wait_duration,
10671084
)
@@ -1114,6 +1131,7 @@ def test_all_args_resolve_image_repos(
11141131
signing_profiles=self.signing_profiles,
11151132
resolve_image_repos=True,
11161133
disable_rollback=self.disable_rollback,
1134+
import_existing_resources=self.import_existing_resources,
11171135
on_failure=self.on_failure,
11181136
max_wait_duration=self.max_wait_duration,
11191137
)
@@ -1141,6 +1159,7 @@ def test_all_args_resolve_image_repos(
11411159
signing_profiles=self.signing_profiles,
11421160
use_changeset=True,
11431161
disable_rollback=self.disable_rollback,
1162+
import_existing_resources=self.import_existing_resources,
11441163
poll_delay=5,
11451164
on_failure=self.on_failure,
11461165
max_wait_duration=self.max_wait_duration,
@@ -1189,6 +1208,7 @@ def test_passing_parameter_overrides_to_context(
11891208
config_file=self.config_file,
11901209
resolve_image_repos=self.resolve_image_repos,
11911210
disable_rollback=self.disable_rollback,
1211+
import_existing_resources=self.import_existing_resources,
11921212
on_failure=self.on_failure,
11931213
max_wait_duration=self.max_wait_duration,
11941214
)
@@ -1216,6 +1236,7 @@ def test_passing_parameter_overrides_to_context(
12161236
signing_profiles=self.signing_profiles,
12171237
use_changeset=self.use_changeset,
12181238
disable_rollback=self.disable_rollback,
1239+
import_existing_resources=self.import_existing_resources,
12191240
poll_delay=os.getenv("SAM_CLI_POLL_DELAY"),
12201241
on_failure=self.on_failure,
12211242
max_wait_duration=self.max_wait_duration,

0 commit comments

Comments
 (0)