runbooks 0.2.5__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conftest.py +26 -0
- jupyter-agent/.env +2 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/.gradio/certificate.pem +31 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/__main__.log +8 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
- jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
- jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
- jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/README.md +337 -0
- runbooks/finops/__init__.py +86 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/aws_organization.png +0 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +1004 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security/README.md +447 -0
- runbooks/security/__init__.py +71 -0
- runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
- runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
- runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
- runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
- runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
- runbooks/{security_baseline → security}/config-origin.json +1 -1
- runbooks/{security_baseline → security}/config.json +1 -1
- runbooks/{security_baseline → security}/permission.json +1 -1
- runbooks/{security_baseline → security}/report_generator.py +10 -2
- runbooks/{security_baseline → security}/report_template_en.html +7 -7
- runbooks/{security_baseline → security}/report_template_jp.html +7 -7
- runbooks/{security_baseline → security}/report_template_kr.html +12 -12
- runbooks/{security_baseline → security}/report_template_vn.html +7 -7
- runbooks/{security_baseline → security}/run_script.py +8 -2
- runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
- runbooks/{security_baseline → security}/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.7.0.dist-info/METADATA +375 -0
- runbooks-0.7.0.dist-info/RECORD +249 -0
- {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
- runbooks-0.7.0.dist-info/entry_points.txt +7 -0
- runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
- runbooks-0.7.0.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.5.dist-info/METADATA +0 -439
- runbooks-0.2.5.dist-info/RECORD +0 -61
- runbooks-0.2.5.dist-info/entry_points.txt +0 -3
- runbooks-0.2.5.dist-info/top_level.txt +0 -1
- /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
- /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
- /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
- /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
- /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
- /runbooks/{security_baseline → security}/utils/enums.py +0 -0
- /runbooks/{security_baseline → security}/utils/language.py +0 -0
- /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
- /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
@@ -0,0 +1,247 @@
|
|
1
|
+
"""
|
2
|
+
AWS resource models for inventory system.
|
3
|
+
|
4
|
+
This module provides Pydantic models for representing individual AWS
|
5
|
+
resources with proper validation, metadata, and cost information.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from dataclasses import dataclass
|
9
|
+
from datetime import datetime
|
10
|
+
from enum import Enum
|
11
|
+
from typing import Any, Dict, List, Optional, Union
|
12
|
+
|
13
|
+
from pydantic import BaseModel, Field, validator
|
14
|
+
|
15
|
+
|
16
|
+
class ResourceState(str, Enum):
|
17
|
+
"""AWS resource state enumeration."""
|
18
|
+
|
19
|
+
RUNNING = "running"
|
20
|
+
STOPPED = "stopped"
|
21
|
+
PENDING = "pending"
|
22
|
+
TERMINATED = "terminated"
|
23
|
+
AVAILABLE = "available"
|
24
|
+
UNAVAILABLE = "unavailable"
|
25
|
+
IN_USE = "in-use"
|
26
|
+
CREATING = "creating"
|
27
|
+
DELETING = "deleting"
|
28
|
+
UNKNOWN = "unknown"
|
29
|
+
|
30
|
+
|
31
|
+
class ResourceType(str, Enum):
|
32
|
+
"""Supported AWS resource types."""
|
33
|
+
|
34
|
+
# Compute
|
35
|
+
EC2_INSTANCE = "ec2:instance"
|
36
|
+
LAMBDA_FUNCTION = "lambda:function"
|
37
|
+
ECS_CLUSTER = "ecs:cluster"
|
38
|
+
ECS_SERVICE = "ecs:service"
|
39
|
+
ECS_TASK = "ecs:task"
|
40
|
+
|
41
|
+
# Storage
|
42
|
+
S3_BUCKET = "s3:bucket"
|
43
|
+
EBS_VOLUME = "ebs:volume"
|
44
|
+
EBS_SNAPSHOT = "ebs:snapshot"
|
45
|
+
EFS_FILESYSTEM = "efs:filesystem"
|
46
|
+
|
47
|
+
# Database
|
48
|
+
RDS_INSTANCE = "rds:instance"
|
49
|
+
RDS_CLUSTER = "rds:cluster"
|
50
|
+
DYNAMODB_TABLE = "dynamodb:table"
|
51
|
+
ELASTICACHE_CLUSTER = "elasticache:cluster"
|
52
|
+
|
53
|
+
# Network
|
54
|
+
VPC = "vpc:vpc"
|
55
|
+
SUBNET = "vpc:subnet"
|
56
|
+
SECURITY_GROUP = "vpc:security-group"
|
57
|
+
LOAD_BALANCER = "elb:load-balancer"
|
58
|
+
ENI = "ec2:network-interface"
|
59
|
+
|
60
|
+
# Security & Identity
|
61
|
+
IAM_USER = "iam:user"
|
62
|
+
IAM_ROLE = "iam:role"
|
63
|
+
IAM_POLICY = "iam:policy"
|
64
|
+
GUARDDUTY_DETECTOR = "guardduty:detector"
|
65
|
+
|
66
|
+
# Management & Governance
|
67
|
+
CLOUDFORMATION_STACK = "cloudformation:stack"
|
68
|
+
CLOUDFORMATION_STACKSET = "cloudformation:stackset"
|
69
|
+
CONFIG_RECORDER = "config:recorder"
|
70
|
+
CLOUDTRAIL_TRAIL = "cloudtrail:trail"
|
71
|
+
|
72
|
+
|
73
|
+
@dataclass
|
74
|
+
class ResourceCost:
|
75
|
+
"""Cost information for an AWS resource."""
|
76
|
+
|
77
|
+
monthly_cost: Optional[float] = None
|
78
|
+
currency: str = "USD"
|
79
|
+
cost_center: Optional[str] = None
|
80
|
+
billing_period: Optional[str] = None
|
81
|
+
cost_breakdown: Dict[str, float] = None
|
82
|
+
|
83
|
+
def __post_init__(self):
|
84
|
+
"""Initialize cost breakdown if not provided."""
|
85
|
+
if self.cost_breakdown is None:
|
86
|
+
self.cost_breakdown = {}
|
87
|
+
|
88
|
+
|
89
|
+
class ResourceMetadata(BaseModel):
|
90
|
+
"""
|
91
|
+
Metadata for AWS resource collection and management.
|
92
|
+
|
93
|
+
Provides context about when and how the resource was discovered,
|
94
|
+
along with collection-specific information.
|
95
|
+
"""
|
96
|
+
|
97
|
+
account_id: str = Field(..., pattern=r"^\d{12}$", description="AWS account ID where resource exists")
|
98
|
+
|
99
|
+
region: str = Field(..., description="AWS region where resource is located")
|
100
|
+
|
101
|
+
service_category: str = Field(..., description="AWS service category (compute, storage, etc.)")
|
102
|
+
|
103
|
+
collection_timestamp: datetime = Field(..., description="When this resource was discovered")
|
104
|
+
|
105
|
+
collector_version: Optional[str] = Field(None, description="Version of collector that discovered this resource")
|
106
|
+
|
107
|
+
raw_data: Dict[str, Any] = Field(default_factory=dict, description="Raw AWS API response data")
|
108
|
+
|
109
|
+
discovery_method: str = Field("api", description="How this resource was discovered (api, cloudtrail, etc.)")
|
110
|
+
|
111
|
+
confidence_score: float = Field(1.0, ge=0.0, le=1.0, description="Confidence in resource data accuracy (0.0-1.0)")
|
112
|
+
|
113
|
+
class Config:
|
114
|
+
"""Pydantic configuration."""
|
115
|
+
|
116
|
+
json_encoders = {datetime: lambda v: v.isoformat()}
|
117
|
+
|
118
|
+
|
119
|
+
class AWSResource(BaseModel):
|
120
|
+
"""
|
121
|
+
Comprehensive model for AWS resources.
|
122
|
+
|
123
|
+
Represents any AWS resource with standardized fields for identification,
|
124
|
+
state management, cost tracking, and metadata.
|
125
|
+
"""
|
126
|
+
|
127
|
+
# Core identification
|
128
|
+
resource_id: str = Field(..., description="AWS resource identifier (instance ID, bucket name, etc.)")
|
129
|
+
|
130
|
+
resource_type: str = Field(..., description="Type of AWS resource")
|
131
|
+
|
132
|
+
resource_arn: Optional[str] = Field(None, description="Amazon Resource Name (ARN) if available")
|
133
|
+
|
134
|
+
resource_name: Optional[str] = Field(None, description="Human-readable resource name")
|
135
|
+
|
136
|
+
# State and lifecycle
|
137
|
+
state: ResourceState = Field(ResourceState.UNKNOWN, description="Current resource state")
|
138
|
+
|
139
|
+
creation_date: Optional[datetime] = Field(None, description="Resource creation timestamp")
|
140
|
+
|
141
|
+
last_modified_date: Optional[datetime] = Field(None, description="Last modification timestamp")
|
142
|
+
|
143
|
+
# Location and organization
|
144
|
+
account_id: str = Field(..., pattern=r"^\d{12}$", description="AWS account ID")
|
145
|
+
|
146
|
+
region: str = Field(..., description="AWS region")
|
147
|
+
|
148
|
+
availability_zone: Optional[str] = Field(None, description="Availability zone if applicable")
|
149
|
+
|
150
|
+
# Configuration and properties
|
151
|
+
configuration: Dict[str, Any] = Field(default_factory=dict, description="Resource-specific configuration details")
|
152
|
+
|
153
|
+
tags: Dict[str, str] = Field(default_factory=dict, description="Resource tags")
|
154
|
+
|
155
|
+
# Security and compliance
|
156
|
+
security_groups: List[str] = Field(default_factory=list, description="Associated security group IDs")
|
157
|
+
|
158
|
+
encryption_status: Optional[str] = Field(None, description="Encryption status (encrypted, not-encrypted, unknown)")
|
159
|
+
|
160
|
+
public_access: bool = Field(False, description="Whether resource has public internet access")
|
161
|
+
|
162
|
+
# Cost information
|
163
|
+
cost_info: Optional[ResourceCost] = Field(None, description="Cost information if available")
|
164
|
+
|
165
|
+
# Metadata
|
166
|
+
metadata: ResourceMetadata = Field(..., description="Collection and discovery metadata")
|
167
|
+
|
168
|
+
# Relationships
|
169
|
+
dependencies: List[str] = Field(default_factory=list, description="List of resource ARNs this resource depends on")
|
170
|
+
|
171
|
+
dependents: List[str] = Field(
|
172
|
+
default_factory=list, description="List of resource ARNs that depend on this resource"
|
173
|
+
)
|
174
|
+
|
175
|
+
class Config:
|
176
|
+
"""Pydantic configuration."""
|
177
|
+
|
178
|
+
use_enum_values = True
|
179
|
+
json_encoders = {datetime: lambda v: v.isoformat() if v else None}
|
180
|
+
|
181
|
+
@validator("resource_arn")
|
182
|
+
def validate_arn_format(cls, v):
|
183
|
+
"""Validate ARN format if provided."""
|
184
|
+
if v and not v.startswith("arn:aws:"):
|
185
|
+
raise ValueError('ARN must start with "arn:aws:"')
|
186
|
+
return v
|
187
|
+
|
188
|
+
@validator("account_id")
|
189
|
+
def validate_account_id(cls, v):
|
190
|
+
"""Validate account ID format."""
|
191
|
+
if not v.isdigit() or len(v) != 12:
|
192
|
+
raise ValueError("Account ID must be exactly 12 digits")
|
193
|
+
return v
|
194
|
+
|
195
|
+
def is_active(self) -> bool:
|
196
|
+
"""Check if resource is in an active state."""
|
197
|
+
active_states = {ResourceState.RUNNING, ResourceState.AVAILABLE, ResourceState.IN_USE}
|
198
|
+
return self.state in active_states
|
199
|
+
|
200
|
+
def is_billable(self) -> bool:
|
201
|
+
"""Check if resource is likely to incur costs."""
|
202
|
+
# Most resources are billable unless explicitly stopped/terminated
|
203
|
+
non_billable_states = {ResourceState.STOPPED, ResourceState.TERMINATED, ResourceState.UNAVAILABLE}
|
204
|
+
return self.state not in non_billable_states
|
205
|
+
|
206
|
+
def get_cost_estimate(self) -> float:
|
207
|
+
"""Get monthly cost estimate for this resource."""
|
208
|
+
if self.cost_info and self.cost_info.monthly_cost:
|
209
|
+
return self.cost_info.monthly_cost
|
210
|
+
return 0.0
|
211
|
+
|
212
|
+
def has_tag(self, key: str, value: Optional[str] = None) -> bool:
|
213
|
+
"""Check if resource has a specific tag."""
|
214
|
+
if key not in self.tags:
|
215
|
+
return False
|
216
|
+
if value is None:
|
217
|
+
return True
|
218
|
+
return self.tags[key] == value
|
219
|
+
|
220
|
+
def get_tag(self, key: str, default: str = "") -> str:
|
221
|
+
"""Get tag value with optional default."""
|
222
|
+
return self.tags.get(key, default)
|
223
|
+
|
224
|
+
def add_dependency(self, resource_arn: str) -> None:
|
225
|
+
"""Add a resource dependency."""
|
226
|
+
if resource_arn not in self.dependencies:
|
227
|
+
self.dependencies.append(resource_arn)
|
228
|
+
|
229
|
+
def add_dependent(self, resource_arn: str) -> None:
|
230
|
+
"""Add a dependent resource."""
|
231
|
+
if resource_arn not in self.dependents:
|
232
|
+
self.dependents.append(resource_arn)
|
233
|
+
|
234
|
+
def get_service_name(self) -> str:
|
235
|
+
"""Extract AWS service name from resource type."""
|
236
|
+
if ":" in self.resource_type:
|
237
|
+
return self.resource_type.split(":")[0]
|
238
|
+
return self.resource_type
|
239
|
+
|
240
|
+
def to_dict(self) -> Dict[str, Any]:
|
241
|
+
"""Convert to dictionary representation."""
|
242
|
+
return self.dict()
|
243
|
+
|
244
|
+
def __str__(self) -> str:
|
245
|
+
"""String representation."""
|
246
|
+
name = self.resource_name or self.resource_id
|
247
|
+
return f"AWSResource({self.resource_type}: {name})"
|
@@ -0,0 +1,205 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import sys
|
5
|
+
from os.path import split
|
6
|
+
from pprint import pprint
|
7
|
+
from time import time
|
8
|
+
|
9
|
+
import Inventory_Modules
|
10
|
+
import simplejson as json
|
11
|
+
from account_class import aws_acct_access
|
12
|
+
from ArgumentsClass import CommonArguments
|
13
|
+
from colorama import Fore, init
|
14
|
+
from Inventory_Modules import get_credentials_for_accounts_in_org
|
15
|
+
|
16
|
+
"""
|
17
|
+
This script was created to help solve a testing problem for the "move_stack_instances.py" script.e
|
18
|
+
Originally, that script didn't have built-in recovery, so we needed this script to "recover" those stack-instance ids that might have been lost during the move_stack_instances.py run. However, that script now has built-in recovery, so this script isn't really needed. However, it can still be used to find any stack-instances that have been orphaned from their original stack-set, if that happens.
|
19
|
+
"""
|
20
|
+
|
21
|
+
init()
|
22
|
+
__version__ = "2024.05.18"
|
23
|
+
|
24
|
+
|
25
|
+
#########################
|
26
|
+
# Functions
|
27
|
+
#########################
|
28
|
+
|
29
|
+
|
30
|
+
def parse_args(args):
|
31
|
+
"""
|
32
|
+
Description: Parse the arguments sent to the script
|
33
|
+
@param args: namespace of the arguments passed in at the command line
|
34
|
+
@return: namespace with all parameters parsed out
|
35
|
+
"""
|
36
|
+
script_path, script_name = split(sys.argv[0])
|
37
|
+
parser = CommonArguments()
|
38
|
+
parser.singleprofile() # Allows for a single profile to be specified
|
39
|
+
parser.singleregion() # Allows for a single region to be specified at the command line
|
40
|
+
parser.extendedargs() # Allows for extended arguments like which accounts to skip, and whether Force is enabled.
|
41
|
+
parser.fragment()
|
42
|
+
parser.timing()
|
43
|
+
parser.rolestouse()
|
44
|
+
parser.verbosity() # Allows for the verbosity to be handled.
|
45
|
+
parser.version(__version__)
|
46
|
+
local = parser.my_parser.add_argument_group(script_name, "Parameters specific to this script")
|
47
|
+
# local.add_argument(
|
48
|
+
# "-s", "--status",
|
49
|
+
# dest="status",
|
50
|
+
# metavar="CloudFormation status",
|
51
|
+
# default="active",
|
52
|
+
# help="String that determines whether we only see 'CREATE_COMPLETE' or 'DELETE_COMPLETE' too")
|
53
|
+
local.add_argument(
|
54
|
+
"-R", "--SearchRegions", dest="pRegionList", metavar="Region name", help="Regions where StackSets are applied"
|
55
|
+
)
|
56
|
+
local.add_argument("--new", dest="newStackSetName", metavar="New Stackset name", help="The NEW Stack Set name")
|
57
|
+
local.add_argument("--old", dest="oldStackSetName", metavar="Old Stackset name", help="The OLD Stack Set name")
|
58
|
+
return parser.my_parser.parse_args(args)
|
59
|
+
|
60
|
+
|
61
|
+
def setup_auth_and_regions(fProfile: str = None) -> (object, list, list):
|
62
|
+
"""
|
63
|
+
Description: This function takes in a profile, and returns the account object and the regions valid for this account / org.
|
64
|
+
@param fProfile: A string representing the profile provided by the user. If nothing, then use the default profile or credentials.
|
65
|
+
@return: (aws_acct_access, list)
|
66
|
+
"""
|
67
|
+
aws_acct = aws_acct_access(fProfile)
|
68
|
+
ChildAccounts = aws_acct.ChildAccounts
|
69
|
+
RegionList = Inventory_Modules.get_regions3(aws_acct, pRegionList)
|
70
|
+
ChildAccounts = Inventory_Modules.RemoveCoreAccounts(ChildAccounts, pAccountsToSkip)
|
71
|
+
AccountList = [account["AccountId"] for account in ChildAccounts]
|
72
|
+
|
73
|
+
print(f"You asked to find stacks with this fragment list: {Fore.RED}{pFragments}{Fore.RESET}")
|
74
|
+
print(f"\t\tin these accounts: {Fore.RED}{AccountList}{Fore.RESET}")
|
75
|
+
print(f"\t\tin these regions: {Fore.RED}{RegionList}{Fore.RESET}")
|
76
|
+
return aws_acct, AccountList, RegionList
|
77
|
+
|
78
|
+
|
79
|
+
#########################
|
80
|
+
# Main
|
81
|
+
#########################
|
82
|
+
|
83
|
+
if __name__ == "__main__":
|
84
|
+
args = parse_args(sys.argv[1:])
|
85
|
+
pProfile = args.Profile
|
86
|
+
pRegion = args.Region
|
87
|
+
pRegionList = args.pRegionList
|
88
|
+
pAccountsToSkip = args.SkipAccounts
|
89
|
+
pAccounts = args.Accounts
|
90
|
+
pOldStackSetName = args.oldStackSetName
|
91
|
+
pNewStackSetName = args.newStackSetName
|
92
|
+
pTiming = args.Time
|
93
|
+
pFragments = args.Fragments
|
94
|
+
# pstatus = args.status
|
95
|
+
verbose = args.loglevel
|
96
|
+
# Setup logging levels
|
97
|
+
logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
98
|
+
logging.getLogger("boto3").setLevel(logging.CRITICAL)
|
99
|
+
logging.getLogger("botocore").setLevel(logging.CRITICAL)
|
100
|
+
logging.getLogger("s3transfer").setLevel(logging.CRITICAL)
|
101
|
+
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
|
102
|
+
|
103
|
+
##########################
|
104
|
+
ERASE_LINE = "\x1b[2K"
|
105
|
+
begin_time = time()
|
106
|
+
|
107
|
+
# Setup credentials and regions (filtered by what they wanted to check)
|
108
|
+
aws_acct, AccountList, RegionList = setup_auth_and_regions(pProfile)
|
109
|
+
|
110
|
+
pStackIdFlag = True
|
111
|
+
precoveryFlag = True
|
112
|
+
|
113
|
+
print()
|
114
|
+
fmt = "%-20s %-15s %-15s %-50s %-50s"
|
115
|
+
print(fmt % ("Account", "Region", "Stack Status", "Stack Name", "Stack ID"))
|
116
|
+
print(fmt % ("-------", "------", "------------", "----------", "--------"))
|
117
|
+
|
118
|
+
StacksFound = []
|
119
|
+
sts_client = aws_acct.session.client("sts")
|
120
|
+
item_counter = 0
|
121
|
+
AllCredentials = get_credentials_for_accounts_in_org(aws_acct)
|
122
|
+
for cred in AllCredentials:
|
123
|
+
if not cred["Success"]:
|
124
|
+
continue
|
125
|
+
for region in RegionList:
|
126
|
+
item_counter += 1
|
127
|
+
Stacks = False
|
128
|
+
try:
|
129
|
+
Stacks = Inventory_Modules.find_stacks2(cred, region, pFragments)
|
130
|
+
logging.warning(f"Account: {cred['AccountId']} | Region: {region} | Found {len(Stacks)} Stacks")
|
131
|
+
print(
|
132
|
+
f"{ERASE_LINE}{Fore.RED}Account: {cred['AccountId']} Region: {region} Found {len(Stacks)} Stacks{Fore.RESET} ({item_counter} of {len(AccountList) * len(RegionList)})",
|
133
|
+
end="\r",
|
134
|
+
)
|
135
|
+
except Exception as my_Error:
|
136
|
+
print(f"Error: {my_Error}")
|
137
|
+
# TODO: Currently we're using this "Stacks" list as a boolean if it's populated. We should change this.
|
138
|
+
if Stacks and len(Stacks) > 0:
|
139
|
+
for y in range(len(Stacks)):
|
140
|
+
StackName = Stacks[y]["StackName"]
|
141
|
+
StackStatus = Stacks[y]["StackStatus"]
|
142
|
+
StackID = Stacks[y]["StackId"]
|
143
|
+
if pStackIdFlag:
|
144
|
+
print(fmt % (cred["AccountId"], region, StackStatus, StackName, StackID))
|
145
|
+
else:
|
146
|
+
print(fmt % (cred["AccountId"], region, StackStatus, StackName))
|
147
|
+
StacksFound.append(
|
148
|
+
{
|
149
|
+
"Account": cred["AccountId"],
|
150
|
+
"Region": region,
|
151
|
+
"StackName": StackName,
|
152
|
+
"StackStatus": StackStatus,
|
153
|
+
"StackArn": StackID,
|
154
|
+
}
|
155
|
+
)
|
156
|
+
lAccounts = []
|
157
|
+
lRegions = []
|
158
|
+
lAccountsAndRegions = []
|
159
|
+
for i in range(len(StacksFound)):
|
160
|
+
lAccounts.append(StacksFound[i]["Account"])
|
161
|
+
lRegions.append(StacksFound[i]["Region"])
|
162
|
+
lAccountsAndRegions.append((StacksFound[i]["Account"], StacksFound[i]["Region"]))
|
163
|
+
print(ERASE_LINE)
|
164
|
+
print(
|
165
|
+
f"{Fore.RED}Looked through {len(StacksFound)} Stacks across {len(AccountList)} accounts across {len(RegionList)} regions{Fore.RESET}"
|
166
|
+
)
|
167
|
+
print()
|
168
|
+
if args.loglevel < 21: # INFO level
|
169
|
+
print("The list of accounts and regions:")
|
170
|
+
pprint(list(sorted(set(lAccountsAndRegions))))
|
171
|
+
|
172
|
+
if precoveryFlag:
|
173
|
+
Stack_Instances = []
|
174
|
+
for item in StacksFound:
|
175
|
+
Stack_Instances.append(
|
176
|
+
{
|
177
|
+
"Account": item["Account"],
|
178
|
+
"Region": item["Region"],
|
179
|
+
"Status": item["StackStatus"],
|
180
|
+
"StackId": item["StackArn"],
|
181
|
+
}
|
182
|
+
)
|
183
|
+
stack_ids = {"Stack_instances": Stack_Instances, "Success": True}
|
184
|
+
BigString = {
|
185
|
+
"AccountNumber": aws_acct.acct_number,
|
186
|
+
"AccountToMove": None,
|
187
|
+
"ManagementAccount": aws_acct.MgmtAccount,
|
188
|
+
"NewStackSetName": pNewStackSetName,
|
189
|
+
"OldStackSetName": pOldStackSetName,
|
190
|
+
"ProfileUsed": pProfile,
|
191
|
+
"Region": pRegion,
|
192
|
+
"stack_ids": stack_ids,
|
193
|
+
}
|
194
|
+
file_data = json.dumps(BigString, sort_keys=True, indent=4 * " ")
|
195
|
+
OutputFilename = f"{pOldStackSetName}-{pNewStackSetName}-{aws_acct.acct_number}-{pRegion}"
|
196
|
+
with open(OutputFilename, "w") as out:
|
197
|
+
print(file_data, file=out)
|
198
|
+
|
199
|
+
if pTiming:
|
200
|
+
print(ERASE_LINE)
|
201
|
+
print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
|
202
|
+
|
203
|
+
print()
|
204
|
+
print("Thanks for using this script...")
|
205
|
+
print()
|
@@ -0,0 +1,211 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import sys
|
5
|
+
|
6
|
+
import Inventory_Modules
|
7
|
+
from account_class import aws_acct_access
|
8
|
+
from ArgumentsClass import CommonArguments
|
9
|
+
from botocore.exceptions import ClientError
|
10
|
+
from colorama import Fore, init
|
11
|
+
|
12
|
+
init()
|
13
|
+
__version__ = "2023.05.04"
|
14
|
+
|
15
|
+
parser = CommonArguments()
|
16
|
+
parser.singleprofile()
|
17
|
+
parser.verbosity() # Allows for the verbosity to be handled.
|
18
|
+
parser.version(__version__)
|
19
|
+
parser.my_parser.add_argument(
|
20
|
+
"-f",
|
21
|
+
"--file",
|
22
|
+
dest="pAccountFile",
|
23
|
+
metavar="Account File",
|
24
|
+
required=True,
|
25
|
+
default=None,
|
26
|
+
help="List of account numbers, one per line.",
|
27
|
+
)
|
28
|
+
parser.my_parser.add_argument(
|
29
|
+
"--Role",
|
30
|
+
dest="pAccessRole",
|
31
|
+
metavar="Access Role",
|
32
|
+
default="Admin",
|
33
|
+
help="Role used to gain access to the list of accounts.",
|
34
|
+
)
|
35
|
+
args = parser.my_parser.parse_args()
|
36
|
+
|
37
|
+
pProfile = args.Profile
|
38
|
+
pAccountFile = args.pAccountFile
|
39
|
+
pAccessRole = args.pAccessRole
|
40
|
+
verbose = args.loglevel
|
41
|
+
logging.basicConfig(level=args.loglevel, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
42
|
+
|
43
|
+
aws_acct = aws_acct_access(pProfile)
|
44
|
+
|
45
|
+
#####################
|
46
|
+
|
47
|
+
|
48
|
+
def check_account_access(faws_acct, faccount_num, fAccessRole=None):
|
49
|
+
if fAccessRole is None:
|
50
|
+
logging.error(f"Role must be provided")
|
51
|
+
return_response = {"Success": False, "ErrorMessage": "Role wasn't provided"}
|
52
|
+
return return_response
|
53
|
+
sts_client = faws_acct.session.client("sts")
|
54
|
+
try:
|
55
|
+
role_arn = f"arn:aws:iam::{faccount_num}:role/{fAccessRole}"
|
56
|
+
credentials = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="TheOtherGuy")["Credentials"]
|
57
|
+
return_response = {"Credentials": credentials, "Success": True, "ErrorMessage": ""}
|
58
|
+
return return_response
|
59
|
+
except ClientError as my_Error:
|
60
|
+
print(f"Client Error: {my_Error}")
|
61
|
+
return_response = {"Success": False, "ErrorMessage": "Client Error"}
|
62
|
+
return return_response
|
63
|
+
except sts_client.exceptions.MalformedPolicyDocumentException as my_Error:
|
64
|
+
print(f"MalformedPolicy: {my_Error}")
|
65
|
+
return_response = {"Success": False, "ErrorMessage": "Malformed Policy"}
|
66
|
+
return return_response
|
67
|
+
except sts_client.exceptions.PackedPolicyTooLargeException as my_Error:
|
68
|
+
print(f"Policy is too large: {my_Error}")
|
69
|
+
return_response = {"Success": False, "ErrorMessage": "Policy is too large"}
|
70
|
+
return return_response
|
71
|
+
except sts_client.exceptions.RegionDisabledException as my_Error:
|
72
|
+
print(f"Region is disabled: {my_Error}")
|
73
|
+
return_response = {"Success": False, "ErrorMessage": "Region Disabled"}
|
74
|
+
return return_response
|
75
|
+
except sts_client.exceptions.ExpiredTokenException as my_Error:
|
76
|
+
print(f"Expired Token: {my_Error}")
|
77
|
+
return_response = {"Success": False, "ErrorMessage": "Expired Token"}
|
78
|
+
return return_response
|
79
|
+
|
80
|
+
|
81
|
+
def participant_user(faws_acct, create=None, username=None):
|
82
|
+
from randominfo import random_password
|
83
|
+
|
84
|
+
if create is None:
|
85
|
+
create = True
|
86
|
+
if username is None:
|
87
|
+
username = "reinvent_admin"
|
88
|
+
password = random_password()
|
89
|
+
client_iam = faws_acct.session.client("iam")
|
90
|
+
return_response = {
|
91
|
+
"Success": False,
|
92
|
+
"ErrorMessage": None,
|
93
|
+
"AccountId": None,
|
94
|
+
"User": None,
|
95
|
+
"Password": None,
|
96
|
+
"AccessKeyId": None,
|
97
|
+
"SecretAccessKey": None,
|
98
|
+
}
|
99
|
+
if create:
|
100
|
+
try:
|
101
|
+
user_response = client_iam.create_user(UserName=username)["User"]
|
102
|
+
return_response = {"Success": True, "AccountId": faws_acct.acct_number, "User": username, "Password": None}
|
103
|
+
|
104
|
+
except client_iam.exceptions.EntityAlreadyExistsException as my_Error:
|
105
|
+
logging.error(f"User {username} already exists... moving on")
|
106
|
+
try:
|
107
|
+
# user_response = client_iam.create_user(UserName=username)['User']
|
108
|
+
password_response = client_iam.create_login_profile(
|
109
|
+
UserName=username, Password=password, PasswordResetRequired=False
|
110
|
+
)
|
111
|
+
return_response = {
|
112
|
+
"Success": True,
|
113
|
+
"AccountId": faws_acct.acct_number,
|
114
|
+
"User": username,
|
115
|
+
"Password": password,
|
116
|
+
}
|
117
|
+
except client_iam.exceptions.EntityAlreadyExistsException as my_Error:
|
118
|
+
logging.error("User login profile already exists, so we'll update it instead")
|
119
|
+
try:
|
120
|
+
password_response = client_iam.update_login_profile(
|
121
|
+
UserName=username, Password=password, PasswordResetRequired=False
|
122
|
+
)
|
123
|
+
return_response = {
|
124
|
+
"Success": True,
|
125
|
+
"AccountId": faws_acct.acct_number,
|
126
|
+
"User": username,
|
127
|
+
"Password": password,
|
128
|
+
}
|
129
|
+
except ClientError as my_Error:
|
130
|
+
ErrorMessage = f"Client Error: {my_Error}"
|
131
|
+
logging.error(f"ErrorMessage: {ErrorMessage}")
|
132
|
+
return_response = {"Success": False, "ErrorMessage": ErrorMessage}
|
133
|
+
except ClientError as my_Error:
|
134
|
+
ErrorMessage = f"Client Error: {my_Error}"
|
135
|
+
logging.error(f"ErrorMessage: {ErrorMessage}")
|
136
|
+
return_response = {"Success": False, "ErrorMessage": ErrorMessage}
|
137
|
+
try:
|
138
|
+
access_keys = client_iam.create_access_key(UserName=username)["AccessKey"]
|
139
|
+
return_response["AccessKeyId"] = access_keys["AccessKeyId"]
|
140
|
+
return_response["SecretAccessKey"] = access_keys["SecretAccessKey"]
|
141
|
+
except (
|
142
|
+
ClientError,
|
143
|
+
client_iam.exceptions.NoSuchEntityException,
|
144
|
+
client_iam.exceptions.LimitExceededException,
|
145
|
+
client_iam.exceptions.ServiceFailureException,
|
146
|
+
) as my_Error:
|
147
|
+
ErrorMessage = f"Client Error: {my_Error}"
|
148
|
+
logging.error(f"ErrorMessage: {ErrorMessage}")
|
149
|
+
return_response["Success"] = False
|
150
|
+
return_response["ErrorMessage"] = ErrorMessage
|
151
|
+
try:
|
152
|
+
attached_policy = client_iam.attach_user_policy(
|
153
|
+
UserName=username, PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess"
|
154
|
+
)
|
155
|
+
return_response["Success"] = True
|
156
|
+
except (
|
157
|
+
client_iam.exceptions.EntityTemporarilyUnmodifiableException,
|
158
|
+
client_iam.exceptions.NoSuchEntityException,
|
159
|
+
client_iam.exceptions.PasswordPolicyViolationException,
|
160
|
+
client_iam.exceptions.PasswordPolicyViolationException,
|
161
|
+
client_iam.exceptions.LimitExceededException,
|
162
|
+
client_iam.exceptions.ServiceFailureException,
|
163
|
+
) as my_Error:
|
164
|
+
ErrorMessage = f"Specific Error: {my_Error}"
|
165
|
+
logging.error(f"ErrorMessage: {ErrorMessage}")
|
166
|
+
return_response["Success"] = False
|
167
|
+
return_response["ErrorMessage"] = ErrorMessage
|
168
|
+
return return_response
|
169
|
+
|
170
|
+
|
171
|
+
#####################
|
172
|
+
|
173
|
+
|
174
|
+
if pAccountFile is None:
|
175
|
+
logging.critical(f"file of accounts was not provided. This is required. Exiting.")
|
176
|
+
sys.exit(1)
|
177
|
+
|
178
|
+
Accounts = []
|
179
|
+
with open(pAccountFile, "r") as infile:
|
180
|
+
for line in infile:
|
181
|
+
Accounts.append(line.rstrip("\r\n,"))
|
182
|
+
infile.close()
|
183
|
+
|
184
|
+
for account_num in Accounts:
|
185
|
+
logging.info(
|
186
|
+
f"Accessing account #{account_num} as {pAccessRole} using account's {aws_acct.acct_number}'s credentials"
|
187
|
+
)
|
188
|
+
response = check_account_access(aws_acct, account_num, pAccessRole)
|
189
|
+
if response["Success"]:
|
190
|
+
print(f"Account {account_num} was successfully connected via role {pAccessRole} from {aws_acct.acct_number}")
|
191
|
+
"""
|
192
|
+
Put more commands here... Or you can write functions that represent your commands and call them from here.
|
193
|
+
"""
|
194
|
+
credentials = Inventory_Modules.get_child_access3(aws_acct, account_num, "us-east-1", ["reinvent-Admin"])
|
195
|
+
tgt_aws_access = aws_acct_access(ocredentials=credentials)
|
196
|
+
username = "Paul"
|
197
|
+
user_response = participant_user(tgt_aws_access, username=username)
|
198
|
+
else:
|
199
|
+
print(
|
200
|
+
f"Access Role {pAccessRole} failed to connect to {account_num} from {aws_acct.acct_number} with error: {response['ErrorMessage']}"
|
201
|
+
)
|
202
|
+
|
203
|
+
# Display access keys
|
204
|
+
print(f"Credentials for account {tgt_aws_access.acct_number}")
|
205
|
+
print(f"User {username} has been created (or confirmed) in account {user_response['AccountId']}")
|
206
|
+
print(f"Password for {user_response['User']} is {user_response['Password']}")
|
207
|
+
print(f"Access Keys are:\n{user_response['AccessKeyId']}\n{user_response['SecretAccessKey']}")
|
208
|
+
|
209
|
+
print()
|
210
|
+
print("Thanks for using this script...")
|
211
|
+
print()
|