Skip to content

Instantly share code, notes, and snippets.

@pitbulk
Last active May 12, 2020 03:43
Show Gist options
  • Save pitbulk/2b4b27b647ad332a2b5bc13f0c104295 to your computer and use it in GitHub Desktop.
Save pitbulk/2b4b27b647ad332a2b5bc13f0c104295 to your computer and use it in GitHub Desktop.
StackSet template for deploying Onelogin with AWS Control Tower
AWSTemplateFormatVersion: 2010-09-09
Description: Create OneLogin IDP, Role to allow OL extract account role list and default roles on all accounts.
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: "OneLogin SAML IDP"
Parameters:
- OLMetadataUrl
- OLExternalId
-
Label:
default: "Roles"
Parameters:
- EnableAdminRole
- AdminRoleName
- EnablePowerUserRole
- PowerUserRoleName
- EnableReadOnlyRole
- ReadOnlyRoleName
Mappings:
OLMapping:
IDPName:
Name: OLIdP
RoleName:
Name: OLGetRoles
Parameters:
OLMetadataUrl:
Type: String
Description: Publicly accessible HTTPS location where SAML metadata.xml can be downloaded.
OLExternalId:
Type: String
Description: (Optional) OneLogin External ID generated at the OL App Configuration tab used to in. Used in the Onelogin integration that extracts the roles from AWS.
AdminRoleName:
Type: String
Description: Role name for administrator access.
Default: AdministratorAccessRole
PowerUserRoleName:
Type: String
Description: Role name for administaror but IAM access.
Default: PowerUserAccessRole
ReadOnlyRoleName:
Type: String
Description: Role name for read-only access.
Default: ReadOnlyAccessRole
EnableAdminRole:
Type: String
Default: 'true'
Description: Create an administrative role.
AllowedValues:
- 'true'
- 'false'
EnablePowerUserRole:
Type: String
Default: 'true'
Description: Create an administrative but IAM role.
AllowedValues:
- 'true'
- 'false'
EnableReadOnlyRole:
Type: String
Default: 'true'
Description: Create a read-only role.
AllowedValues:
- 'true'
- 'false'
Conditions:
CreateAdminRole: !Equals
- !Ref EnableAdminRole
- 'true'
CreatePowerUserRole: !Equals
- !Ref EnablePowerUserRole
- 'true'
CreateReadOnlyRole: !Equals
- !Ref EnableReadOnlyRole
- 'true'
Resources:
AdminRole:
Type: AWS::IAM::Role
Condition: CreateAdminRole
Properties:
RoleName: !Ref AdminRoleName
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Federated:
- !GetAtt SAMLIdp.SAMLProviderArn
Action:
- sts:AssumeRoleWithSAML
Condition:
StringEquals:
'SAML:aud':
'https://signin.aws.amazon.com/saml'
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
PowerUserRole:
Type: AWS::IAM::Role
Condition: CreatePowerUserRole
Properties:
RoleName: !Ref PowerUserRoleName
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Federated:
- !GetAtt SAMLIdp.SAMLProviderArn
Action:
- sts:AssumeRoleWithSAML
Condition:
StringEquals:
'SAML:aud':
'https://signin.aws.amazon.com/saml'
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/PowerUserAccess
ReadOnlyRole:
Type: AWS::IAM::Role
Condition: CreateReadOnlyRole
Properties:
RoleName: !Ref ReadOnlyRoleName
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Federated:
- !GetAtt SAMLIdp.SAMLProviderArn
Action:
- sts:AssumeRoleWithSAML
Condition:
StringEquals:
'SAML:aud':
'https://signin.aws.amazon.com/saml'
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/ReadOnlyAccess
OLListRolesRole:
Type: AWS::IAM::Role
Properties:
RoleName: !FindInMap [OLMapping, "RoleName", "Name"]
Policies:
- PolicyName: listroles
PolicyDocument:
Statement:
- Effect: Allow
Action:
- iam:ListRoles
- iam:ListAccountAliases
Resource: '*'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS: 842984801698
Action:
- sts:AssumeRole
Condition:
StringEquals:
'sts:ExternalId':
!Ref OLExternalId
Path: /
SAMLIdpLambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: 'lambda.amazonaws.com'
Action:
- 'sts:AssumeRole'
Path: '/'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Policies:
- PolicyName: createupdate
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'iam:CreateSAMLProvider'
- 'iam:UpdateSAMLProvider'
- 'iam:DeleteSAMLProvider'
Resource: !Join
- ''
- - 'arn:aws:iam::*:saml-provider/'
- !FindInMap [OLMapping, "IDPName", "Name"]
- PolicyName: list
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'iam:ListSAMLProviders'
Resource: '*'
SAMLIdpLambda:
Type: "AWS::Lambda::Function"
Properties:
Handler: "index.handler"
Role:
Fn::GetAtt:
- "SAMLIdpLambdaRole"
- "Arn"
Code:
ZipFile: >
var cfr = require('./cfn-response');
var AWS = require('aws-sdk');
var iam = new AWS.IAM();
var https = require('https');
var fs = require('fs');
exports.handler = function(event, context) {
console.log(event);
var resID = event.PhysicalResourceId || event.RequestId;
var metadataUrl = event.ResourceProperties.metadataUrl;
var idpName = event.ResourceProperties.idpName;
var filename = '/tmp/metadata.xml'
var file = fs.createWriteStream(filename);
if (event.RequestType == 'Create' || event.RequestType == 'Update') {
var request = https.get(metadataUrl, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close( function () {
let rawdata = fs.readFileSync(filename, 'utf8');
if (event.RequestType == 'Update') {
var paramslist = {};
iam.listSAMLProviders(paramslist, function(err, data) {
if (err) cfr.send(event, context, cfr.FAILED, err, resID);
else {
var arn='';
for (var provider in data.SAMLProviderList) {
if (data.SAMLProviderList[provider].Arn.split('/')[1] == idpName) {
console.log('Update provider. ' + data.SAMLProviderList[provider].Arn + ' already exists.')
arn = data.SAMLProviderList[provider].Arn;
}
}
if (arn == '') {
var params = { Name: idpName, SAMLMetadataDocument: rawdata };
iam.createSAMLProvider(params, function(err, data) {
if (err) cfr.send(event, context, cfr.FAILED, err, resID);
else cfr.send(event, context, cfr.SUCCESS, data, resID);
});
} else {
var params = { SAMLProviderArn: arn, SAMLMetadataDocument: rawdata };
iam.updateSAMLProvider(params, function(err, data) {
if (err) cfr.send(event, context, cfr.FAILED, err, resID);
else cfr.send(event, context, cfr.SUCCESS, data, resID);
});
}
}
});
} else {
var params = { Name: idpName, SAMLMetadataDocument: rawdata };
iam.createSAMLProvider(params, function(err, data) {
if (err) cfr.send(event, context, cfr.FAILED, err, resID);
else cfr.send(event, context, cfr.SUCCESS, data, resID);
});
}
});
});
}).on('error', function(err) {
fs.unlink(filename);
cfr.send(event, context, cfr.FAILED, err);
});
} else if (event.RequestType == 'Delete') {
var paramslist = {};
iam.listSAMLProviders(paramslist, function(err, data) {
if (err) cfr.send(event, context, cfr.FAILED, err, resID);
else {
var deleteResource = false;
var arn;
console.log(data.SAMLProviderList);
for (var provider in data.SAMLProviderList) {
if (data.SAMLProviderList[provider].Arn.split('/')[1] == idpName) {
console.log('Delete provider. ' + data.SAMLProviderList[provider].Arn + ' already exists.')
deleteResource = true;
arn = data.SAMLProviderList[provider].Arn;
}
}
var params = { SAMLProviderArn: arn };
if (deleteResource == true) {
iam.deleteSAMLProvider(params, function(err, data) {
if (err) cfr.send(event, context, cfr.FAILED, err, resID);
else cfr.send(event, context, cfr.SUCCESS, data, resID);
});
} else {
cfr.send(event, context, cfr.SUCCESS, {success: 'Provider did not exist'}, resID);
}
}
});
} else {
cfr.send(event, context, cfr.FAILED, { error: 'Method not supported'}, resID);
}
};
Runtime: "nodejs10.x"
Timeout: "30"
SAMLIdp:
Type: Custom::SAMLIdp
Properties:
ServiceToken: !GetAtt SAMLIdpLambda.Arn
metadataUrl: !Ref OLMetadataUrl
idpName: !FindInMap [OLMapping, "IDPName", "Name"]
@pitbulk
Copy link
Author

pitbulk commented Jan 9, 2020

@fss18 How do you see those load errors? How do you debug the lambda function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment