Skip to content

Commit e35d7ff

Browse files
authored
Support large account lists (#158)
* Convert inline Step Function payload to file for very lasrge account lists * Mod new Licence Manager module to use v2 of common state machine
1 parent fea0e98 commit e35d7ff

File tree

3 files changed

+61
-31
lines changed

3 files changed

+61
-31
lines changed

data-collection/deploy/account-collector.yaml

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Resources:
8585
- Effect: "Allow"
8686
Action:
8787
- "s3:GetObject"
88+
- "s3:PutObject"
8889
Resource:
8990
- !Sub "${DestinationBucketARN}/*"
9091
Metadata:
@@ -113,7 +114,10 @@ Resources:
113114
RESOURCE_PREFIX = os.environ['RESOURCE_PREFIX']
114115
MANAGEMENT_ACCOUNT_IDS = os.environ['MANAGEMENT_ACCOUNT_IDS']
115116
BUCKET = os.environ['BUCKET_NAME']
116-
ACCOUNT_LIST_KEY = os.environ['ACCOUNT_LIST_KEY']
117+
PREDEF_ACCOUNT_LIST_KEY = os.environ['PREDEF_ACCOUNT_LIST_KEY']
118+
LINKED_ACCOUNT_LIST_KEY = os.environ['LINKED_ACCOUNT_LIST_KEY']
119+
PAYER_ACCOUNT_LIST_KEY = os.environ['PAYER_ACCOUNT_LIST_KEY']
120+
TMP_FILE = "/tmp/data.json"
117121
118122
logger = logging.getLogger(__name__)
119123
logger.setLevel(getattr(logging, os.environ.get('LOG_LEVEL', 'INFO').upper(), logging.INFO))
@@ -141,10 +145,25 @@ Resources:
141145
raise Exception(f"Lambda event must have 'Type' parameter with value = ({list(functions.keys())})") #pylint: disable=broad-exception-raised
142146
143147
account_iterator = functions[account_type]
144-
accounts = list(account_iterator())
145-
if not accounts:
148+
149+
with open(TMP_FILE, "w") as f:
150+
count = 0
151+
f.write("[\n")
152+
for account in account_iterator():
153+
if count > 0:
154+
f.write(",\n")
155+
f.write(json.dumps(account))
156+
count += 1
157+
f.write("\n]")
158+
159+
if count == 0:
146160
raise Exception('No accounts found. Check the log.') #pylint: disable=broad-exception-raised
147-
return {'statusCode': 200, 'accountList': accounts}
161+
162+
key = LINKED_ACCOUNT_LIST_KEY if account_type == 'linked' else PAYER_ACCOUNT_LIST_KEY
163+
s3 = boto3.client('s3')
164+
s3.upload_file(TMP_FILE, Bucket=BUCKET, Key=key)
165+
166+
return {'statusCode': 200, 'accountList': key, 'bucket': BUCKET}
148167
149168
def get_all_payers():
150169
for payer_id in MANAGEMENT_ACCOUNT_IDS.split(','):
@@ -163,10 +182,10 @@ Resources:
163182
yield {"account": json.dumps({'account_id': account_id, 'account_name': '', 'payer_id': payer_id})}
164183
165184
def iterate_linked_accounts():
166-
defined_accounts, ext = get_defined_list(BUCKET, ACCOUNT_LIST_KEY)
185+
defined_accounts, ext = get_defined_list(BUCKET, PREDEF_ACCOUNT_LIST_KEY)
167186
try:
168187
if defined_accounts:
169-
logger.info(f'Using defined account list {ACCOUNT_LIST_KEY}.{ext} instead of payer organization')
188+
logger.info(f'Using defined account list instead of payer organization')
170189
for account_data in defined_accounts:
171190
if ext == "json":
172191
account = json.loads(account_data)
@@ -186,10 +205,10 @@ Resources:
186205
187206
def get_defined_list(bucket, key):
188207
s3 = boto3.client("s3")
189-
exts = ["json", "csv"]
208+
exts = [".json", ".csv"]
190209
for ext in exts:
191210
try:
192-
accts = s3.get_object(Bucket=bucket, Key=f"{key}.{ext}")
211+
accts = s3.get_object(Bucket=bucket, Key=f"{key}{ext}")
193212
return accts['Body'].read().decode('utf-8').strip('\n').split('\n'), ext
194213
except Exception as exc: #pylint: disable=broad-exception-caught
195214
continue
@@ -205,6 +224,7 @@ Resources:
205224
})
206225
}
207226
227+
208228
def get_client_with_role(account_id, service, region):
209229
credentials = boto3.client('sts').assume_role(
210230
RoleArn=f"arn:aws:iam::{account_id}:role/{ROLE_NAME}",
@@ -217,7 +237,6 @@ Resources:
217237
aws_secret_access_key=credentials['SecretAccessKey'],
218238
aws_session_token=credentials['SessionToken'],
219239
)
220-
221240
Handler: 'index.lambda_handler'
222241
MemorySize: 2688
223242
Timeout: 600
@@ -228,7 +247,9 @@ Resources:
228247
MANAGEMENT_ACCOUNT_IDS: !Ref ManagementAccountID
229248
RESOURCE_PREFIX: !Ref ResourcePrefix
230249
BUCKET_NAME: !Ref DestinationBucket
231-
ACCOUNT_LIST_KEY: "account-list/account-list"
250+
PREDEF_ACCOUNT_LIST_KEY: "account-list/account-list"
251+
LINKED_ACCOUNT_LIST_KEY: "account-list/linked-account-list.json"
252+
PAYER_ACCOUNT_LIST_KEY: "account-list/payer-account-list.json"
232253
Metadata:
233254
cfn_nag:
234255
rules_to_suppress:

data-collection/deploy/deploy-data-collection.yaml

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ Mappings:
106106
us-west-1: {CodeBucket: aws-managed-cost-intelligence-dashboards-us-west-1 }
107107
us-west-2: {CodeBucket: aws-managed-cost-intelligence-dashboards-us-west-2 }
108108
StepFunctionCode:
109-
main-v1: {TemplatePath: cfn/data-collection/source/step-functions/main-state-machine-v1.json}
109+
main-v2: {TemplatePath: cfn/data-collection/source/step-functions/main-state-machine-v2.json}
110110
crawler-v1: {TemplatePath: cfn/data-collection/source/step-functions/crawler-state-machine-v1.json}
111111
awsfeeds-v1: {TemplatePath: cfn/data-collection/source/step-functions/awsfeeds-state-machine-v1.json}
112112

@@ -800,8 +800,7 @@ Resources:
800800
Action:
801801
- states:StartExecution
802802
Resource:
803-
- !Sub 'arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${ResourcePrefix}CrawlerExecution-StateMachine'
804-
- !Sub 'arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${ResourcePrefix}*detail-StateMachine'
803+
- !Sub 'arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${ResourcePrefix}*-StateMachine'
805804
- Effect: Allow
806805
Action:
807806
- states:DescribeExecution
@@ -884,7 +883,7 @@ Resources:
884883
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
885884
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
886885
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
887-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
886+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
888887
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
889888
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
890889

@@ -904,7 +903,7 @@ Resources:
904903
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
905904
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
906905
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
907-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
906+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
908907
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
909908
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
910909

@@ -924,7 +923,7 @@ Resources:
924923
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
925924
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
926925
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
927-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
926+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
928927
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
929928
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
930929

@@ -944,7 +943,7 @@ Resources:
944943
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
945944
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
946945
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
947-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
946+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
948947
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
949948
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
950949

@@ -964,7 +963,7 @@ Resources:
964963
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
965964
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
966965
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
967-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
966+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
968967
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
969968
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
970969

@@ -984,7 +983,7 @@ Resources:
984983
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
985984
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
986985
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
987-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
986+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
988987
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
989988
LambdaManageGlueTableARN: !GetAtt LambdaManageGlueTable.Arn
990989
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
@@ -1036,7 +1035,7 @@ Resources:
10361035
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
10371036
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
10381037
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1039-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1038+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
10401039
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
10411040
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
10421041

@@ -1056,7 +1055,7 @@ Resources:
10561055
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
10571056
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
10581057
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1059-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1058+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
10601059
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
10611060
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
10621061
RegionsInScope:
@@ -1081,7 +1080,7 @@ Resources:
10811080
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
10821081
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
10831082
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1084-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1083+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
10851084
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
10861085
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
10871086
RegionsInScope:
@@ -1106,7 +1105,7 @@ Resources:
11061105
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
11071106
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
11081107
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1109-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1108+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
11101109
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
11111110
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
11121111

@@ -1126,7 +1125,7 @@ Resources:
11261125
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
11271126
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
11281127
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1129-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1128+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
11301129
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
11311130
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
11321131

@@ -1146,7 +1145,7 @@ Resources:
11461145
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
11471146
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
11481147
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1149-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1148+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
11501149
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
11511150
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
11521151
RegionsInScope:
@@ -1189,7 +1188,7 @@ Resources:
11891188
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
11901189
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
11911190
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1192-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1191+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
11931192
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
11941193
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
11951194

@@ -1209,7 +1208,7 @@ Resources:
12091208
LambdaAnalyticsARN: !GetAtt LambdaAnalytics.Arn
12101209
AccountCollectorLambdaARN: !Sub "${AccountCollector.Outputs.LambdaFunctionARN}"
12111210
CodeBucket: !If [ ProdCFNTemplateUsed, !FindInMap [RegionMap, !Ref "AWS::Region", CodeBucket], !Ref CFNSourceBucket ]
1212-
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v1, TemplatePath]
1211+
StepFunctionTemplate: !FindInMap [StepFunctionCode, main-v2, TemplatePath]
12131212
StepFunctionExecutionRoleARN: !GetAtt StepFunctionExecutionRole.Arn
12141213
SchedulerExecutionRoleARN: !GetAtt SchedulerExecutionRole.Arn
12151214

data-collection/deploy/source/step-functions/main-state-machine-v1.json renamed to data-collection/deploy/source/step-functions/main-state-machine-v2.json

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"Type": "Map",
3232
"ItemProcessor": {
3333
"ProcessorConfig": {
34-
"Mode": "INLINE"
34+
"Mode": "DISTRIBUTED",
35+
"ExecutionType": "STANDARD"
3536
},
3637
"StartAt": "InvokeModuleLambda",
3738
"States": {
@@ -63,9 +64,18 @@
6364
}
6465
}
6566
},
66-
"ItemsPath": "$.accountLambdaOutput.Payload.accountList",
67-
"Next": "CrawlerStepFunctionStartExecution",
68-
"MaxConcurrency": 60
67+
"MaxConcurrency": 60,
68+
"ItemReader": {
69+
"Resource": "arn:aws:states:::s3:getObject",
70+
"ReaderConfig": {
71+
"InputType": "JSON"
72+
},
73+
"Parameters": {
74+
"Bucket.$": "$.accountLambdaOutput.Payload.bucket",
75+
"Key.$": "$.accountLambdaOutput.Payload.accountList"
76+
}
77+
},
78+
"Next": "CrawlerStepFunctionStartExecution"
6979
},
7080
"CrawlerStepFunctionStartExecution": {
7181
"Type": "Task",

0 commit comments

Comments
 (0)