Repository: cloud-custodian/cloud-custodian

Issue:
Add support for SageMaker Hyperparameter Tuning Job resource
### Describe the feature

Would be useful to support SageMaker Hyperparameter Tuning Jobs as a aws.sagemaker-hyperparameter-tuning-job resource type

### Extra information or context

_No response_


Discussion:

Resolving patch:
diff --git a/c7n/resources/resource_map.py b/c7n/resources/resource_map.py
--- a/c7n/resources/resource_map.py
+++ b/c7n/resources/resource_map.py
@@ -229,6 +229,8 @@
   "aws.sagemaker-model": "c7n.resources.sagemaker.Model",
   "aws.sagemaker-notebook": "c7n.resources.sagemaker.NotebookInstance",
   "aws.sagemaker-transform-job": "c7n.resources.sagemaker.SagemakerTransformJob",
+  "aws.sagemaker-hyperparameter-tuning-job":
+    "c7n.resources.sagemaker.SagemakerHyperParameterTuningJob",
   "aws.scaling-policy": "c7n.resources.asg.ScalingPolicy",
   "aws.secrets-manager": "c7n.resources.secretsmanager.SecretsManager",
   "aws.security-group": "c7n.resources.vpc.SecurityGroup",
diff --git a/c7n/resources/sagemaker.py b/c7n/resources/sagemaker.py
--- a/c7n/resources/sagemaker.py
+++ b/c7n/resources/sagemaker.py
@@ -137,6 +137,44 @@ def _augment(j):
         return list(map(_augment, super(SagemakerTransformJob, self).augment(jobs)))
 
 
+class SagemakerHyperParameterTuningJobDescribe(DescribeSource):
+
+    def augment(self, resources):
+        return universal_augment(self.manager, super().augment(resources))
+
+
+@resources.register('sagemaker-hyperparameter-tuning-job')
+class SagemakerHyperParameterTuningJob(QueryResourceManager):
+    class resource_type(TypeInfo):
+        service = 'sagemaker'
+        enum_spec = ('list_hyper_parameter_tuning_jobs', 'HyperParameterTuningJobSummaries', None)
+        detail_spec = (
+            'describe_hyper_parameter_tuning_job', 'HyperParameterTuningJobName',
+            'HyperParameterTuningJobName', None)
+        arn = id = 'HyperParameterTuningJobArn'
+        name = 'HyperParameterTuningJobName'
+        date = 'CreationTime'
+        permission_prefix = 'sagemaker'
+        universal_taggable = object()
+
+    source_mapping = {'describe': SagemakerHyperParameterTuningJobDescribe}
+
+    def __init__(self, ctx, data):
+        super(SagemakerHyperParameterTuningJob, self).__init__(ctx, data)
+        self.queries = QueryFilter.parse(
+            self.data.get('query', [
+                {'StatusEquals': 'InProgress'}]))
+
+    def resources(self, query=None):
+        for q in self.queries:
+            if q is None:
+                continue
+            query = query or {}
+            for k, v in q.items():
+                query[k] = v
+        return super(SagemakerHyperParameterTuningJob, self).resources(query=query)
+
+
 class SagemakerAutoMLDescribeV2(DescribeSource):
 
     def get_permissions(self):
@@ -824,6 +862,35 @@ def process(self, jobs):
                 pass
 
 
+@SagemakerHyperParameterTuningJob.action_registry.register('stop')
+class SagemakerHyperParameterTuningJobStop(BaseAction):
+    """Stops a SageMaker Hyperparameter Tuning job
+
+    :example:
+
+    .. code-block:: yaml
+
+        policies:
+          - name: stop-hyperparameter-tuning-job
+            resource: sagemaker-hyperparameter-tuning-job
+            filters:
+              - HyperParameterTuningJobName: ml-job-10
+            actions:
+              - stop
+    """
+    schema = type_schema('stop')
+    permissions = ('sagemaker:StopHyperParameterTuningJob',)
+
+    def process(self, jobs):
+        client = local_session(self.manager.session_factory).client('sagemaker')
+
+        for j in jobs:
+            try:
+                client.stop_hyper_parameter_tuning_job(HyperParameterTuningJobName=j['HyperParameterTuningJobName'])
+            except client.exceptions.ResourceNotFound:
+                pass
+
+
 @SagemakerAutoMLJob.action_registry.register('stop')
 class SagemakerAutoMLJobStop(BaseAction):
     """Stops a SageMaker AutoML job


Test patch:
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/api.sagemaker.DescribeHyperParameterTuningJob_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/api.sagemaker.DescribeHyperParameterTuningJob_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/api.sagemaker.DescribeHyperParameterTuningJob_1.json
@@ -0,0 +1,172 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobName": "c7n-test",
+        "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+        "HyperParameterTuningJobConfig": {
+            "Strategy": "Bayesian",
+            "ResourceLimits": {
+                "MaxNumberOfTrainingJobs": 2,
+                "MaxParallelTrainingJobs": 2
+            }
+        },
+        "TrainingJobDefinitions": [
+            {
+                "DefinitionName": "c7n-test-definition",
+                "TuningObjective": {
+                    "Type": "Minimize",
+                    "MetricName": "test:msd"
+                },
+                "HyperParameterRanges": {
+                    "IntegerParameterRanges": [
+                        {
+                            "Name": "mini_batch_size",
+                            "MinValue": "2",
+                            "MaxValue": "3",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "ContinuousParameterRanges": [],
+                    "CategoricalParameterRanges": [
+                        {
+                            "Name": "init_method",
+                            "Values": [
+                                "random",
+                                "kmeans++"
+                            ]
+                        }
+                    ]
+                },
+                "StaticHyperParameters": {
+                    "_tuning_objective_metric": "test:msd",
+                    "eval_metrics": "[\"msd\"]",
+                    "feature_dim": "2",
+                    "half_life_time_size": "0",
+                    "k": "2",
+                    "local_lloyd_init_method": "kmeans++",
+                    "local_lloyd_max_iter": "300",
+                    "local_lloyd_num_trials": "auto",
+                    "local_lloyd_tol": "0.0001"
+                },
+                "AlgorithmSpecification": {
+                    "TrainingImage": "644160558196.dkr.ecr.us-east-1.amazonaws.com/kmeans:1",
+                    "TrainingInputMode": "File",
+                    "MetricDefinitions": [
+                        {
+                            "Name": "train:progress",
+                            "Regex": "#progress_metric: host=\\S+, completed (\\S+) %"
+                        },
+                        {
+                            "Name": "test:msd",
+                            "Regex": "#quality_metric: host=\\S+, test msd <loss>=(\\S+)"
+                        },
+                        {
+                            "Name": "train:msd",
+                            "Regex": "#quality_metric: host=\\S+, train msd <loss>=(\\S+)"
+                        },
+                        {
+                            "Name": "train:throughput",
+                            "Regex": "#throughput_metric: host=\\S+, train throughput=(\\S+) records/second"
+                        },
+                        {
+                            "Name": "test:ssd",
+                            "Regex": "#quality_metric: host=\\S+, test ssd <loss>=(\\S+)"
+                        },
+                        {
+                            "Name": "ObjectiveMetric",
+                            "Regex": "#quality_metric: host=\\S+, test msd <loss>=(\\S+)"
+                        }
+                    ]
+                },
+                "RoleArn": "arn:aws:iam::644160558196:role/service-role/AmazonSageMakerServiceCatalogProductsUseRole",
+                "InputDataConfig": [
+                    {
+                        "ChannelName": "train",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/input",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    }
+                ],
+                "VpcConfig": {
+                    "SecurityGroupIds": [
+                        "sg-03c0b84cdad91744f"
+                    ],
+                    "Subnets": [
+                        "subnet-0bd18cd4df2d0d3b9"
+                    ]
+                },
+                "OutputDataConfig": {
+                    "S3OutputPath": "s3://c7n-test-s3-bucket/output"
+                },
+                "ResourceConfig": {
+                    "InstanceType": "ml.m4.xlarge",
+                    "InstanceCount": 1,
+                    "VolumeSizeInGB": 1
+                },
+                "StoppingCondition": {
+                    "MaxRuntimeInSeconds": 86400
+                },
+                "EnableNetworkIsolation": false,
+                "EnableInterContainerTrafficEncryption": false,
+                "EnableManagedSpotTraining": false
+            }
+        ],
+        "HyperParameterTuningJobStatus": "Failed",
+        "CreationTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 6,
+            "hour": 14,
+            "minute": 22,
+            "second": 8,
+            "microsecond": 179000
+        },
+        "HyperParameterTuningEndTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 6,
+            "hour": 14,
+            "minute": 22,
+            "second": 22,
+            "microsecond": 341000
+        },
+        "LastModifiedTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 6,
+            "hour": 14,
+            "minute": 22,
+            "second": 22,
+            "microsecond": 341000
+        },
+        "TrainingJobStatusCounters": {
+            "Completed": 0,
+            "InProgress": 0,
+            "RetryableError": 0,
+            "NonRetryableError": 2,
+            "Stopped": 0
+        },
+        "ObjectiveStatusCounters": {
+            "Succeeded": 0,
+            "Pending": 0,
+            "Failed": 2
+        },
+        "FailureReason": "All training jobs failed. Please take a look at the training jobs failures to get more details.",
+        "TuningJobCompletionDetails": {
+            "NumberOfTrainingJobsObjectiveNotImproving": 0
+        },
+        "ConsumedResources": {
+            "RuntimeInSeconds": 11
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/api.sagemaker.ListHyperParameterTuningJobs_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/api.sagemaker.ListHyperParameterTuningJobs_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/api.sagemaker.ListHyperParameterTuningJobs_1.json
@@ -0,0 +1,60 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobSummaries": [
+            {
+                "HyperParameterTuningJobName": "c7n-test",
+                "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+                "HyperParameterTuningJobStatus": "Failed",
+                "Strategy": "Bayesian",
+                "CreationTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 6,
+                    "hour": 14,
+                    "minute": 22,
+                    "second": 8,
+                    "microsecond": 179000
+                },
+                "HyperParameterTuningEndTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 6,
+                    "hour": 14,
+                    "minute": 22,
+                    "second": 22,
+                    "microsecond": 341000
+                },
+                "LastModifiedTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 6,
+                    "hour": 14,
+                    "minute": 22,
+                    "second": 22,
+                    "microsecond": 341000
+                },
+                "TrainingJobStatusCounters": {
+                    "Completed": 0,
+                    "InProgress": 0,
+                    "RetryableError": 0,
+                    "NonRetryableError": 2,
+                    "Stopped": 0
+                },
+                "ObjectiveStatusCounters": {
+                    "Succeeded": 0,
+                    "Pending": 0,
+                    "Failed": 2
+                },
+                "ResourceLimits": {
+                    "MaxNumberOfTrainingJobs": 2,
+                    "MaxParallelTrainingJobs": 2
+                }
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/tagging.GetResources_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_query/tagging.GetResources_1.json
@@ -0,0 +1,18 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+                "Tags": [
+                    {
+                        "Key": "Test",
+                        "Value": "True"
+                    }
+                ]
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.DescribeHyperParameterTuningJob_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.DescribeHyperParameterTuningJob_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.DescribeHyperParameterTuningJob_1.json
@@ -0,0 +1,296 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobName": "c7n-test-4",
+        "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test-4",
+        "HyperParameterTuningJobConfig": {
+            "Strategy": "Bayesian",
+            "ResourceLimits": {
+                "MaxNumberOfTrainingJobs": 20,
+                "MaxParallelTrainingJobs": 3
+            }
+        },
+        "TrainingJobDefinitions": [
+            {
+                "DefinitionName": "c7n-test-def",
+                "TuningObjective": {
+                    "Type": "Maximize",
+                    "MetricName": "validation:auc"
+                },
+                "HyperParameterRanges": {
+                    "IntegerParameterRanges": [
+                        {
+                            "Name": "num_round",
+                            "MinValue": "0",
+                            "MaxValue": "100",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "max_depth",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "ContinuousParameterRanges": [
+                        {
+                            "Name": "eta",
+                            "MinValue": "0",
+                            "MaxValue": "1",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "min_child_weight",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "alpha",
+                            "MinValue": "0",
+                            "MaxValue": "2",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "CategoricalParameterRanges": []
+                },
+                "StaticHyperParameters": {
+                    "_tuning_objective_metric": "validation:auc",
+                    "base_score": "0.5",
+                    "booster": "gbtree",
+                    "csv_weights": "0",
+                    "dsplit": "row",
+                    "grow_policy": "depthwise",
+                    "lambda_bias": "0.0",
+                    "max_bin": "256",
+                    "max_leaves": "0",
+                    "normalize_type": "tree",
+                    "num_parallel_tree": "1",
+                    "objective": "binary:logistic",
+                    "one_drop": "0",
+                    "process_type": "default",
+                    "rate_drop": "0.3",
+                    "refresh_leaf": "1",
+                    "sample_type": "uniform",
+                    "sampling_method": "uniform",
+                    "scale_pos_weight": "1.0",
+                    "sketch_eps": "0.03",
+                    "skip_drop": "0.0",
+                    "tree_method": "auto",
+                    "tweedie_variance_power": "1.4",
+                    "updater": "grow_colmaker,prune",
+                    "verbosity": "1"
+                },
+                "AlgorithmSpecification": {
+                    "TrainingImage": "644160558196.dkr.ecr.us-east-1.amazonaws.com/sagemaker-xgboost:1.3-1",
+                    "TrainingInputMode": "File",
+                    "MetricDefinitions": [
+                        {
+                            "Name": "train:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:recall",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-recall:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:precision",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-precision:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:balanced_accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-balanced_accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "ObjectiveMetric",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        }
+                    ]
+                },
+                "RoleArn": "arn:aws:iam::644160558196:role/service-role/AmazonSageMakerServiceCatalogProductsUseRole",
+                "InputDataConfig": [
+                    {
+                        "ChannelName": "train",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/train",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    },
+                    {
+                        "ChannelName": "validate",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/validation/",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    }
+                ],
+                "OutputDataConfig": {
+                    "S3OutputPath": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/output"
+                },
+                "ResourceConfig": {
+                    "InstanceType": "ml.m4.xlarge",
+                    "InstanceCount": 2,
+                    "VolumeSizeInGB": 10
+                },
+                "StoppingCondition": {
+                    "MaxRuntimeInSeconds": 43200
+                },
+                "EnableNetworkIsolation": false,
+                "EnableInterContainerTrafficEncryption": false,
+                "EnableManagedSpotTraining": false
+            }
+        ],
+        "HyperParameterTuningJobStatus": "InProgress",
+        "CreationTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 10,
+            "hour": 10,
+            "minute": 39,
+            "second": 40,
+            "microsecond": 517000
+        },
+        "LastModifiedTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 10,
+            "hour": 10,
+            "minute": 40,
+            "second": 53,
+            "microsecond": 757000
+        },
+        "TrainingJobStatusCounters": {
+            "Completed": 0,
+            "InProgress": 3,
+            "RetryableError": 0,
+            "NonRetryableError": 0,
+            "Stopped": 0
+        },
+        "ObjectiveStatusCounters": {
+            "Succeeded": 0,
+            "Pending": 3,
+            "Failed": 0
+        },
+        "TuningJobCompletionDetails": {
+            "NumberOfTrainingJobsObjectiveNotImproving": 0
+        },
+        "ConsumedResources": {
+            "RuntimeInSeconds": 73
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.DescribeHyperParameterTuningJob_2.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.DescribeHyperParameterTuningJob_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.DescribeHyperParameterTuningJob_2.json
@@ -0,0 +1,296 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobName": "c7n-test-4",
+        "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test-4",
+        "HyperParameterTuningJobConfig": {
+            "Strategy": "Bayesian",
+            "ResourceLimits": {
+                "MaxNumberOfTrainingJobs": 20,
+                "MaxParallelTrainingJobs": 3
+            }
+        },
+        "TrainingJobDefinitions": [
+            {
+                "DefinitionName": "c7n-test-def",
+                "TuningObjective": {
+                    "Type": "Maximize",
+                    "MetricName": "validation:auc"
+                },
+                "HyperParameterRanges": {
+                    "IntegerParameterRanges": [
+                        {
+                            "Name": "num_round",
+                            "MinValue": "0",
+                            "MaxValue": "100",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "max_depth",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "ContinuousParameterRanges": [
+                        {
+                            "Name": "eta",
+                            "MinValue": "0",
+                            "MaxValue": "1",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "min_child_weight",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "alpha",
+                            "MinValue": "0",
+                            "MaxValue": "2",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "CategoricalParameterRanges": []
+                },
+                "StaticHyperParameters": {
+                    "_tuning_objective_metric": "validation:auc",
+                    "base_score": "0.5",
+                    "booster": "gbtree",
+                    "csv_weights": "0",
+                    "dsplit": "row",
+                    "grow_policy": "depthwise",
+                    "lambda_bias": "0.0",
+                    "max_bin": "256",
+                    "max_leaves": "0",
+                    "normalize_type": "tree",
+                    "num_parallel_tree": "1",
+                    "objective": "binary:logistic",
+                    "one_drop": "0",
+                    "process_type": "default",
+                    "rate_drop": "0.3",
+                    "refresh_leaf": "1",
+                    "sample_type": "uniform",
+                    "sampling_method": "uniform",
+                    "scale_pos_weight": "1.0",
+                    "sketch_eps": "0.03",
+                    "skip_drop": "0.0",
+                    "tree_method": "auto",
+                    "tweedie_variance_power": "1.4",
+                    "updater": "grow_colmaker,prune",
+                    "verbosity": "1"
+                },
+                "AlgorithmSpecification": {
+                    "TrainingImage": "644160558196.dkr.ecr.us-east-1.amazonaws.com/sagemaker-xgboost:1.3-1",
+                    "TrainingInputMode": "File",
+                    "MetricDefinitions": [
+                        {
+                            "Name": "train:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:recall",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-recall:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:precision",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-precision:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:balanced_accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-balanced_accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "ObjectiveMetric",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        }
+                    ]
+                },
+                "RoleArn": "arn:aws:iam::644160558196:role/service-role/AmazonSageMakerServiceCatalogProductsUseRole",
+                "InputDataConfig": [
+                    {
+                        "ChannelName": "train",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/train",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    },
+                    {
+                        "ChannelName": "validate",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/validation/",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    }
+                ],
+                "OutputDataConfig": {
+                    "S3OutputPath": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/output"
+                },
+                "ResourceConfig": {
+                    "InstanceType": "ml.m4.xlarge",
+                    "InstanceCount": 2,
+                    "VolumeSizeInGB": 10
+                },
+                "StoppingCondition": {
+                    "MaxRuntimeInSeconds": 43200
+                },
+                "EnableNetworkIsolation": false,
+                "EnableInterContainerTrafficEncryption": false,
+                "EnableManagedSpotTraining": false
+            }
+        ],
+        "HyperParameterTuningJobStatus": "Stopping",
+        "CreationTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 10,
+            "hour": 10,
+            "minute": 39,
+            "second": 40,
+            "microsecond": 517000
+        },
+        "LastModifiedTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 10,
+            "hour": 10,
+            "minute": 40,
+            "second": 54,
+            "microsecond": 232000
+        },
+        "TrainingJobStatusCounters": {
+            "Completed": 0,
+            "InProgress": 3,
+            "RetryableError": 0,
+            "NonRetryableError": 0,
+            "Stopped": 0
+        },
+        "ObjectiveStatusCounters": {
+            "Succeeded": 0,
+            "Pending": 3,
+            "Failed": 0
+        },
+        "TuningJobCompletionDetails": {
+            "NumberOfTrainingJobsObjectiveNotImproving": 0
+        },
+        "ConsumedResources": {
+            "RuntimeInSeconds": 73
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.ListHyperParameterTuningJobs_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.ListHyperParameterTuningJobs_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.ListHyperParameterTuningJobs_1.json
@@ -0,0 +1,50 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobSummaries": [
+            {
+                "HyperParameterTuningJobName": "c7n-test-4",
+                "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test-4",
+                "HyperParameterTuningJobStatus": "InProgress",
+                "Strategy": "Bayesian",
+                "CreationTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 10,
+                    "hour": 10,
+                    "minute": 39,
+                    "second": 40,
+                    "microsecond": 517000
+                },
+                "LastModifiedTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 10,
+                    "hour": 10,
+                    "minute": 40,
+                    "second": 43,
+                    "microsecond": 811000
+                },
+                "TrainingJobStatusCounters": {
+                    "Completed": 0,
+                    "InProgress": 3,
+                    "RetryableError": 0,
+                    "NonRetryableError": 0,
+                    "Stopped": 0
+                },
+                "ObjectiveStatusCounters": {
+                    "Succeeded": 0,
+                    "Pending": 3,
+                    "Failed": 0
+                },
+                "ResourceLimits": {
+                    "MaxNumberOfTrainingJobs": 20,
+                    "MaxParallelTrainingJobs": 3
+                }
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.StopHyperParameterTuningJob_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.StopHyperParameterTuningJob_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/api.sagemaker.StopHyperParameterTuningJob_1.json
@@ -0,0 +1,6 @@
+{
+    "status_code": 200,
+    "data": {
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/tagging.GetResources_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_stop/tagging.GetResources_1.json
@@ -0,0 +1,13 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test-4",
+                "Tags": []
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.DescribeHyperParameterTuningJob_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.DescribeHyperParameterTuningJob_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.DescribeHyperParameterTuningJob_1.json
@@ -0,0 +1,297 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobName": "c7n-test",
+        "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+        "HyperParameterTuningJobConfig": {
+            "Strategy": "Bayesian",
+            "ResourceLimits": {
+                "MaxNumberOfTrainingJobs": 20,
+                "MaxParallelTrainingJobs": 3
+            }
+        },
+        "TrainingJobDefinitions": [
+            {
+                "DefinitionName": "c7n-test-def",
+                "TuningObjective": {
+                    "Type": "Maximize",
+                    "MetricName": "validation:auc"
+                },
+                "HyperParameterRanges": {
+                    "IntegerParameterRanges": [
+                        {
+                            "Name": "num_round",
+                            "MinValue": "0",
+                            "MaxValue": "100",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "max_depth",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "ContinuousParameterRanges": [
+                        {
+                            "Name": "eta",
+                            "MinValue": "0",
+                            "MaxValue": "1",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "min_child_weight",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "alpha",
+                            "MinValue": "0",
+                            "MaxValue": "2",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "CategoricalParameterRanges": []
+                },
+                "StaticHyperParameters": {
+                    "_tuning_objective_metric": "validation:auc",
+                    "base_score": "0.5",
+                    "booster": "gbtree",
+                    "csv_weights": "0",
+                    "dsplit": "row",
+                    "eval_metric": "auc",
+                    "grow_policy": "depthwise",
+                    "lambda_bias": "0.0",
+                    "max_bin": "256",
+                    "max_leaves": "0",
+                    "normalize_type": "tree",
+                    "num_parallel_tree": "1",
+                    "objective": "binary:logistic",
+                    "one_drop": "0",
+                    "process_type": "default",
+                    "rate_drop": "0.3",
+                    "refresh_leaf": "1",
+                    "sample_type": "uniform",
+                    "sampling_method": "uniform",
+                    "scale_pos_weight": "1.0",
+                    "sketch_eps": "0.03",
+                    "skip_drop": "0.0",
+                    "tree_method": "auto",
+                    "tweedie_variance_power": "1.4",
+                    "updater": "grow_colmaker,prune",
+                    "verbosity": "1"
+                },
+                "AlgorithmSpecification": {
+                    "TrainingImage": "644160558196.dkr.ecr.us-east-1.amazonaws.com/sagemaker-xgboost:1.3-1",
+                    "TrainingInputMode": "File",
+                    "MetricDefinitions": [
+                        {
+                            "Name": "train:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:recall",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-recall:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:precision",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-precision:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:balanced_accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-balanced_accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "ObjectiveMetric",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        }
+                    ]
+                },
+                "RoleArn": "arn:aws:iam::644160558196:role/service-role/AmazonSageMakerServiceCatalogProductsUseRole",
+                "InputDataConfig": [
+                    {
+                        "ChannelName": "train",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/train",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    },
+                    {
+                        "ChannelName": "validation",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/validation/",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    }
+                ],
+                "OutputDataConfig": {
+                    "S3OutputPath": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/output"
+                },
+                "ResourceConfig": {
+                    "InstanceType": "ml.m4.xlarge",
+                    "InstanceCount": 2,
+                    "VolumeSizeInGB": 10
+                },
+                "StoppingCondition": {
+                    "MaxRuntimeInSeconds": 42300
+                },
+                "EnableNetworkIsolation": false,
+                "EnableInterContainerTrafficEncryption": false,
+                "EnableManagedSpotTraining": false
+            }
+        ],
+        "HyperParameterTuningJobStatus": "InProgress",
+        "CreationTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 11,
+            "hour": 11,
+            "minute": 50,
+            "second": 39,
+            "microsecond": 929000
+        },
+        "LastModifiedTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 11,
+            "hour": 11,
+            "minute": 50,
+            "second": 43,
+            "microsecond": 281000
+        },
+        "TrainingJobStatusCounters": {
+            "Completed": 0,
+            "InProgress": 0,
+            "RetryableError": 0,
+            "NonRetryableError": 0,
+            "Stopped": 0
+        },
+        "ObjectiveStatusCounters": {
+            "Succeeded": 0,
+            "Pending": 0,
+            "Failed": 0
+        },
+        "TuningJobCompletionDetails": {
+            "NumberOfTrainingJobsObjectiveNotImproving": 0
+        },
+        "ConsumedResources": {
+            "RuntimeInSeconds": 0
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.DescribeHyperParameterTuningJob_2.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.DescribeHyperParameterTuningJob_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.DescribeHyperParameterTuningJob_2.json
@@ -0,0 +1,297 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobName": "c7n-test",
+        "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+        "HyperParameterTuningJobConfig": {
+            "Strategy": "Bayesian",
+            "ResourceLimits": {
+                "MaxNumberOfTrainingJobs": 20,
+                "MaxParallelTrainingJobs": 3
+            }
+        },
+        "TrainingJobDefinitions": [
+            {
+                "DefinitionName": "c7n-test-def",
+                "TuningObjective": {
+                    "Type": "Maximize",
+                    "MetricName": "validation:auc"
+                },
+                "HyperParameterRanges": {
+                    "IntegerParameterRanges": [
+                        {
+                            "Name": "num_round",
+                            "MinValue": "0",
+                            "MaxValue": "100",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "max_depth",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "ContinuousParameterRanges": [
+                        {
+                            "Name": "eta",
+                            "MinValue": "0",
+                            "MaxValue": "1",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "min_child_weight",
+                            "MinValue": "1",
+                            "MaxValue": "10",
+                            "ScalingType": "Linear"
+                        },
+                        {
+                            "Name": "alpha",
+                            "MinValue": "0",
+                            "MaxValue": "2",
+                            "ScalingType": "Linear"
+                        }
+                    ],
+                    "CategoricalParameterRanges": []
+                },
+                "StaticHyperParameters": {
+                    "_tuning_objective_metric": "validation:auc",
+                    "base_score": "0.5",
+                    "booster": "gbtree",
+                    "csv_weights": "0",
+                    "dsplit": "row",
+                    "eval_metric": "auc",
+                    "grow_policy": "depthwise",
+                    "lambda_bias": "0.0",
+                    "max_bin": "256",
+                    "max_leaves": "0",
+                    "normalize_type": "tree",
+                    "num_parallel_tree": "1",
+                    "objective": "binary:logistic",
+                    "one_drop": "0",
+                    "process_type": "default",
+                    "rate_drop": "0.3",
+                    "refresh_leaf": "1",
+                    "sample_type": "uniform",
+                    "sampling_method": "uniform",
+                    "scale_pos_weight": "1.0",
+                    "sketch_eps": "0.03",
+                    "skip_drop": "0.0",
+                    "tree_method": "auto",
+                    "tweedie_variance_power": "1.4",
+                    "updater": "grow_colmaker,prune",
+                    "verbosity": "1"
+                },
+                "AlgorithmSpecification": {
+                    "TrainingImage": "644160558196.dkr.ecr.us-east-1.amazonaws.com/sagemaker-xgboost:1.3-1",
+                    "TrainingInputMode": "File",
+                    "MetricDefinitions": [
+                        {
+                            "Name": "train:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mae",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mae:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:recall",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-recall:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:precision",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-precision:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1_binary",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1_binary:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:auc",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:error",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-error:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:poisson-nloglik",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-poisson-nloglik:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:logloss",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-logloss:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:aucpr",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-aucpr:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:balanced_accuracy",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-balanced_accuracy:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:rmse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-rmse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:mse",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-mse:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "validation:ndcg",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-ndcg:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:f1",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-f1:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "train:map",
+                            "Regex": ".*\\[[0-9]+\\].*#011train-map:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        },
+                        {
+                            "Name": "ObjectiveMetric",
+                            "Regex": ".*\\[[0-9]+\\].*#011validation-auc:([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?).*"
+                        }
+                    ]
+                },
+                "RoleArn": "arn:aws:iam::644160558196:role/service-role/AmazonSageMakerServiceCatalogProductsUseRole",
+                "InputDataConfig": [
+                    {
+                        "ChannelName": "train",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/train",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    },
+                    {
+                        "ChannelName": "validation",
+                        "DataSource": {
+                            "S3DataSource": {
+                                "S3DataType": "S3Prefix",
+                                "S3Uri": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/validation/",
+                                "S3DataDistributionType": "FullyReplicated"
+                            }
+                        },
+                        "ContentType": "csv",
+                        "CompressionType": "None",
+                        "RecordWrapperType": "None"
+                    }
+                ],
+                "OutputDataConfig": {
+                    "S3OutputPath": "s3://c7n-test-s3-bucket/DEMO-automatic-model-tuning-xgboost-dm/output"
+                },
+                "ResourceConfig": {
+                    "InstanceType": "ml.m4.xlarge",
+                    "InstanceCount": 2,
+                    "VolumeSizeInGB": 10
+                },
+                "StoppingCondition": {
+                    "MaxRuntimeInSeconds": 42300
+                },
+                "EnableNetworkIsolation": false,
+                "EnableInterContainerTrafficEncryption": false,
+                "EnableManagedSpotTraining": false
+            }
+        ],
+        "HyperParameterTuningJobStatus": "InProgress",
+        "CreationTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 11,
+            "hour": 11,
+            "minute": 50,
+            "second": 39,
+            "microsecond": 929000
+        },
+        "LastModifiedTime": {
+            "__class__": "datetime",
+            "year": 2024,
+            "month": 4,
+            "day": 11,
+            "hour": 11,
+            "minute": 50,
+            "second": 49,
+            "microsecond": 104000
+        },
+        "TrainingJobStatusCounters": {
+            "Completed": 0,
+            "InProgress": 3,
+            "RetryableError": 0,
+            "NonRetryableError": 0,
+            "Stopped": 0
+        },
+        "ObjectiveStatusCounters": {
+            "Succeeded": 0,
+            "Pending": 3,
+            "Failed": 0
+        },
+        "TuningJobCompletionDetails": {
+            "NumberOfTrainingJobsObjectiveNotImproving": 0
+        },
+        "ConsumedResources": {
+            "RuntimeInSeconds": 8
+        },
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListHyperParameterTuningJobs_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListHyperParameterTuningJobs_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListHyperParameterTuningJobs_1.json
@@ -0,0 +1,50 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobSummaries": [
+            {
+                "HyperParameterTuningJobName": "c7n-test",
+                "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+                "HyperParameterTuningJobStatus": "InProgress",
+                "Strategy": "Bayesian",
+                "CreationTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 11,
+                    "hour": 11,
+                    "minute": 50,
+                    "second": 39,
+                    "microsecond": 929000
+                },
+                "LastModifiedTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 11,
+                    "hour": 11,
+                    "minute": 50,
+                    "second": 43,
+                    "microsecond": 281000
+                },
+                "TrainingJobStatusCounters": {
+                    "Completed": 0,
+                    "InProgress": 0,
+                    "RetryableError": 0,
+                    "NonRetryableError": 0,
+                    "Stopped": 0
+                },
+                "ObjectiveStatusCounters": {
+                    "Succeeded": 0,
+                    "Pending": 0,
+                    "Failed": 0
+                },
+                "ResourceLimits": {
+                    "MaxNumberOfTrainingJobs": 20,
+                    "MaxParallelTrainingJobs": 3
+                }
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListHyperParameterTuningJobs_2.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListHyperParameterTuningJobs_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListHyperParameterTuningJobs_2.json
@@ -0,0 +1,50 @@
+{
+    "status_code": 200,
+    "data": {
+        "HyperParameterTuningJobSummaries": [
+            {
+                "HyperParameterTuningJobName": "c7n-test",
+                "HyperParameterTuningJobArn": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+                "HyperParameterTuningJobStatus": "InProgress",
+                "Strategy": "Bayesian",
+                "CreationTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 11,
+                    "hour": 11,
+                    "minute": 50,
+                    "second": 39,
+                    "microsecond": 929000
+                },
+                "LastModifiedTime": {
+                    "__class__": "datetime",
+                    "year": 2024,
+                    "month": 4,
+                    "day": 11,
+                    "hour": 11,
+                    "minute": 50,
+                    "second": 43,
+                    "microsecond": 281000
+                },
+                "TrainingJobStatusCounters": {
+                    "Completed": 0,
+                    "InProgress": 0,
+                    "RetryableError": 0,
+                    "NonRetryableError": 0,
+                    "Stopped": 0
+                },
+                "ObjectiveStatusCounters": {
+                    "Succeeded": 0,
+                    "Pending": 0,
+                    "Failed": 0
+                },
+                "ResourceLimits": {
+                    "MaxNumberOfTrainingJobs": 20,
+                    "MaxParallelTrainingJobs": 3
+                }
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListTags_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListTags_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListTags_1.json
@@ -0,0 +1,12 @@
+{
+    "status_code": 200,
+    "data": {
+        "Tags": [
+            {
+                "Key": "JobTag",
+                "Value": "JobTagValue"
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListTags_2.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListTags_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/api.sagemaker.ListTags_2.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "Tags": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.GetResources_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.GetResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.GetResources_1.json
@@ -0,0 +1,8 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.GetResources_2.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.GetResources_2.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.GetResources_2.json
@@ -0,0 +1,18 @@
+{
+    "status_code": 200,
+    "data": {
+        "PaginationToken": "",
+        "ResourceTagMappingList": [
+            {
+                "ResourceARN": "arn:aws:sagemaker:us-east-1:644160558196:hyper-parameter-tuning-job/c7n-test",
+                "Tags": [
+                    {
+                        "Key": "JobTag",
+                        "Value": "JobTagValue"
+                    }
+                ]
+            }
+        ],
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.TagResources_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.TagResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.TagResources_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "FailedResourcesMap": {},
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.UntagResources_1.json b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.UntagResources_1.json
new file mode 100644
--- /dev/null
+++ b/tests/data/placebo/test_sagemaker_hyperparameter_tuning_job_tag/tagging.UntagResources_1.json
@@ -0,0 +1,7 @@
+{
+    "status_code": 200,
+    "data": {
+        "FailedResourcesMap": {},
+        "ResponseMetadata": {}
+    }
+}
\ No newline at end of file
diff --git a/tests/test_sagemaker.py b/tests/test_sagemaker.py
--- a/tests/test_sagemaker.py
+++ b/tests/test_sagemaker.py
@@ -527,6 +527,79 @@ def test_untag_transform_job(self):
         self.assertEqual(len(tags), 0)
 
 
+class TestSagemakerHyperParameterTuningJob(BaseTest):
+
+    def test_sagemaker_hyperparameter_tuning_job_query(self):
+        session_factory = self.replay_flight_data("test_sagemaker_hyperparameter_tuning_job_query")
+        p = self.load_policy(
+            {
+                "name": "query-hyperparameter-tuning-jobs",
+                "resource": "sagemaker-hyperparameter-tuning-job",
+                "query": [{"StatusEquals": "Failed"}],
+            },
+            session_factory=session_factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+
+    def test_stop_hyperparameter_tuning_job(self):
+        session_factory = self.replay_flight_data("test_sagemaker_hyperparameter_tuning_job_stop")
+        client = session_factory(region="us-east-1").client("sagemaker")
+        p = self.load_policy(
+            {
+                "name": "stop-hyperparameter-tuning-job",
+                "resource": "sagemaker-hyperparameter-tuning-job",
+                "filters": [
+                    {
+                        "type": "value",
+                        "key": "HyperParameterTuningJobName",
+                        "value": "test",
+                        "op": "contains",
+                    }
+                ],
+                "actions": [{"type": "stop"}],
+            },
+            session_factory=session_factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        job = client.describe_hyper_parameter_tuning_job(
+            HyperParameterTuningJobName=resources[0]["HyperParameterTuningJobName"]
+        )
+        self.assertEqual(job["HyperParameterTuningJobStatus"], "Stopping")
+
+    def test_tag_hyperparameter_tuning_job(self):
+        session_factory = self.replay_flight_data("test_sagemaker_hyperparameter_tuning_job_tag")
+        p = self.load_policy(
+            {
+                "name": "tag-hyperparameter-tuning-job",
+                "resource": "sagemaker-hyperparameter-tuning-job",
+                "filters": [{"tag:JobTag": "absent"}],
+                "actions": [{"type": "tag", "key": "JobTag", "value": "JobTagValue"}],
+            },
+            session_factory=session_factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        client = session_factory(region="us-east-1").client("sagemaker")
+        tags = client.list_tags(ResourceArn=resources[0]["HyperParameterTuningJobArn"])["Tags"]
+        self.assertEqual([tags[0]["Key"], tags[0]["Value"]], ["JobTag", "JobTagValue"])
+
+        p = self.load_policy(
+            {
+                "name": "remove-hyperparameter-tuning-job-tag",
+                "resource": "sagemaker-hyperparameter-tuning-job",
+                "filters": [{"tag:JobTag": "JobTagValue"}],
+                "actions": [{"type": "remove-tag", "tags": ["JobTag"]}],
+            },
+            session_factory=session_factory,
+        )
+        resources = p.run()
+        self.assertEqual(len(resources), 1)
+        tags = client.list_tags(ResourceArn=resources[0]["HyperParameterTuningJobArn"])["Tags"]
+        self.assertEqual(len(tags), 0)
+
+
 class TestSageMakerAutoMLJob(BaseTest):
 
     def test_sagemaker_automl_job_query(self):